Repository: aspnet/AspNetWebStack Branch: main Commit: af67fe18a182 Files: 3343 Total size: 17.7 MB Directory structure: gitextract_28ij9x6y/ ├── .appveyor.yml ├── .azuredevops/ │ └── dependabot.yml ├── .config/ │ ├── CredScanSuppressions.json │ └── tsaoptions.json ├── .gitattributes ├── .gitignore ├── .nuget/ │ └── packages.config ├── .travis.yml ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── NuGet.Config ├── README.md ├── Runtime.NetFramework.slnf ├── Runtime.NetStandard.slnf ├── Runtime.msbuild ├── Runtime.sln ├── SECURITY.md ├── Settings.StyleCop ├── Tools.sln ├── azure-pipelines-public.yml ├── azure-pipelines.yml ├── build.cmd ├── eng/ │ ├── GetXCopyMSBuild.ps1 │ └── templates/ │ └── default-build.yml ├── es-metadata.yml ├── global.json ├── src/ │ ├── CodeAnalysisDictionary.xml │ ├── Common/ │ │ ├── AttributeList.cs │ │ ├── CollectionExtensions.cs │ │ ├── CommonWebApiResources.Designer.cs │ │ ├── CommonWebApiResources.resx │ │ ├── DictionaryExtensions.cs │ │ ├── EfficientTypePropertyKey.cs │ │ ├── Empty.cs │ │ ├── Error.cs │ │ ├── HashCodeCombiner.cs │ │ ├── HttpMethodHelper.cs │ │ ├── ListWrapperCollection.cs │ │ ├── NonOwnedStream.cs │ │ ├── PathHelpers.cs │ │ ├── PrefixContainer.cs │ │ ├── PropertyHelper.cs │ │ ├── Routing/ │ │ │ ├── Constraints/ │ │ │ │ ├── AlphaRouteConstraint.cs │ │ │ │ ├── BoolRouteConstraint.cs │ │ │ │ ├── CompoundRouteConstraint.cs │ │ │ │ ├── DateTimeRouteConstraint.cs │ │ │ │ ├── DecimalRouteConstraint.cs │ │ │ │ ├── DoubleRouteConstraint.cs │ │ │ │ ├── FloatRouteConstraint.cs │ │ │ │ ├── GuidRouteConstraint.cs │ │ │ │ ├── IntRouteConstraint.cs │ │ │ │ ├── LengthRouteConstraint.cs │ │ │ │ ├── LongRouteConstraint.cs │ │ │ │ ├── MaxLengthRouteConstraint.cs │ │ │ │ ├── MaxRouteConstraint.cs │ │ │ │ ├── MinLengthRouteConstraint.cs │ │ │ │ ├── MinRouteConstraint.cs │ │ │ │ ├── OptionalRouteConstraint.cs │ │ │ │ ├── RangeRouteConstraintBase.cs │ │ │ │ └── RegexRouteConstraint.cs │ │ │ ├── DefaultInlineConstraintResolver.cs │ │ │ ├── DirectRouteBuilder.cs │ │ │ ├── DirectRouteFactoryContext.cs │ │ │ ├── IDirectRouteBuilder.cs │ │ │ ├── IDirectRouteFactory.cs │ │ │ ├── IDirectRouteProvider.cs │ │ │ ├── IInlineConstraintResolver.cs │ │ │ ├── IRoutePrefix.cs │ │ │ ├── InlineRouteTemplateParser.cs │ │ │ ├── PathContentSegment.cs │ │ │ ├── PathLiteralSubsegment.cs │ │ │ ├── PathParameterSubsegment.cs │ │ │ ├── PathSegment.cs │ │ │ ├── PathSeparatorSegment.cs │ │ │ ├── PathSubsegment.cs │ │ │ ├── RouteEntry.cs │ │ │ ├── RouteFactoryAttribute.cs │ │ │ ├── RouteInfoDirectRouteFactory.cs │ │ │ ├── RouteParser.cs │ │ │ ├── RoutePrecedence.cs │ │ │ └── SubRouteCollection.cs │ │ ├── TaskHelpers.cs │ │ ├── TaskHelpersExtensions.cs │ │ ├── TraceWriterExceptionMapper.cs │ │ └── TypeExtensions.cs │ ├── CommonAssemblyInfo.cs │ ├── CommonAssemblyInfo.vb │ ├── CommonResources.Designer.cs │ ├── CommonResources.resx │ ├── Directory.Build.props │ ├── GlobalSuppressions.cs │ ├── Microsoft.AspNet.Facebook/ │ │ ├── Authorization/ │ │ │ └── FacebookAuthorizeFilter.cs │ │ ├── Client/ │ │ │ ├── FacebookClientExtensions.cs │ │ │ ├── FacebookQueryHelper.cs │ │ │ └── FacebookRequestHelper.cs │ │ ├── FacebookAppSettingKeys.cs │ │ ├── FacebookAuthenticationModule.cs │ │ ├── FacebookAuthorizeAttribute.cs │ │ ├── FacebookConfiguration.cs │ │ ├── FacebookConnection.cs │ │ ├── FacebookContext.cs │ │ ├── FacebookFieldModifierAttribute.cs │ │ ├── FacebookGroupConnection.cs │ │ ├── FacebookHtmlHelperExtensions.cs │ │ ├── FacebookRedirectContext.cs │ │ ├── GlobalFacebookConfiguration.cs │ │ ├── GlobalSuppressions.cs │ │ ├── JavaScriptRedirectResult.cs │ │ ├── Microsoft.AspNet.Facebook.csproj │ │ ├── ModelBinders/ │ │ │ ├── FacebookContextBinderAttribute.cs │ │ │ ├── FacebookContextModelBinder.cs │ │ │ ├── FacebookRedirectContextBinderAttribute.cs │ │ │ └── FacebookRedirectContextModelBinder.cs │ │ ├── Models/ │ │ │ ├── ChangeEntry.cs │ │ │ ├── ChangeNotification.cs │ │ │ └── SubscriptionVerification.cs │ │ ├── PermissionContext.cs │ │ ├── PermissionHelper.cs │ │ ├── PermissionStatus.cs │ │ ├── PermissionsStatus.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Providers/ │ │ │ ├── DefaultFacebookClientProvider.cs │ │ │ ├── DefaultFacebookPermissionService.cs │ │ │ ├── IFacebookClientProvider.cs │ │ │ └── IFacebookPermissionService.cs │ │ ├── Realtime/ │ │ │ └── FacebookRealtimeUpdateController.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── ShowPromptResult.cs │ │ └── packages.config │ ├── Microsoft.Web.Helpers/ │ │ ├── Analytics.cshtml │ │ ├── Facebook.cshtml │ │ ├── FileUpload.cshtml │ │ ├── GamerCard.cshtml │ │ ├── GlobalSuppressions.cs │ │ ├── Gravatar.cs │ │ ├── GravatarRating.cs │ │ ├── LinkShare.cshtml │ │ ├── LinkShareSite.cs │ │ ├── Maps.cshtml │ │ ├── Microsoft.Web.Helpers.csproj │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── ReCaptcha.cshtml │ │ ├── Resources/ │ │ │ ├── HelpersToolkitResources.Designer.cs │ │ │ └── HelpersToolkitResources.resx │ │ ├── Themes.cs │ │ ├── ThemesImplementation.cs │ │ ├── UrlBuilder.cs │ │ ├── Video.cs │ │ ├── VirtualPathUtilityBase.cs │ │ ├── VirtualPathUtilityWrapper.cs │ │ └── packages.config │ ├── Microsoft.Web.Mvc/ │ │ ├── AcceptAttribute.cs │ │ ├── ActionLinkAreaAttribute.cs │ │ ├── AjaxOnlyAttribute.cs │ │ ├── AreaHelpers.cs │ │ ├── AsyncManagerExtensions.cs │ │ ├── ButtonBuilder.cs │ │ ├── ButtonsAndLinkExtensions.cs │ │ ├── CachedExpressionCompiler.cs │ │ ├── ContentTypeAttribute.cs │ │ ├── ControllerExtensions.cs │ │ ├── Controls/ │ │ │ ├── ActionLink.cs │ │ │ ├── DropDownList.cs │ │ │ ├── EncodeType.cs │ │ │ ├── Hidden.cs │ │ │ ├── Label.cs │ │ │ ├── MvcControl.cs │ │ │ ├── MvcInputControl.cs │ │ │ ├── Password.cs │ │ │ ├── Repeater.cs │ │ │ ├── RepeaterItem.cs │ │ │ ├── RouteValues.cs │ │ │ └── TextBox.cs │ │ ├── CookieValueProviderFactory.cs │ │ ├── CopyAsyncParametersAttribute.cs │ │ ├── CreditCardAttribute.cs │ │ ├── CssExtensions.cs │ │ ├── DeserializeAttribute.cs │ │ ├── DynamicReflectionObject.cs │ │ ├── DynamicViewDataDictionary.cs │ │ ├── DynamicViewPage.cs │ │ ├── DynamicViewPageOfTModel.cs │ │ ├── ElementalValueProvider.cs │ │ ├── EmailAddressAttribute.cs │ │ ├── Error.cs │ │ ├── ExpressionUtil/ │ │ │ ├── BinaryExpressionFingerprint.cs │ │ │ ├── CachedExpressionCompiler.cs │ │ │ ├── ConditionalExpressionFingerprint.cs │ │ │ ├── ConstantExpressionFingerprint.cs │ │ │ ├── DefaultExpressionFingerprint.cs │ │ │ ├── ExpressionFingerprint.cs │ │ │ ├── ExpressionFingerprintChain.cs │ │ │ ├── FingerprintingExpressionVisitor.cs │ │ │ ├── HashCodeCombiner.cs │ │ │ ├── Hoisted.cs │ │ │ ├── HoistingExpressionVisitor.cs │ │ │ ├── IndexExpressionFingerprint.cs │ │ │ ├── LambdaExpressionFingerprint.cs │ │ │ ├── MemberExpressionFingerprint.cs │ │ │ ├── MethodCallExpressionFingerprint.cs │ │ │ ├── ParameterExpressionFingerprint.cs │ │ │ ├── TypeBinaryExpressionFingerprint.cs │ │ │ └── UnaryExpressionFingerprint.cs │ │ ├── FileExtensionsAttribute.cs │ │ ├── FormExtensions.cs │ │ ├── FuturesFiles/ │ │ │ ├── DefaultTemplates/ │ │ │ │ ├── DisplayTemplates/ │ │ │ │ │ ├── Boolean.ascx │ │ │ │ │ ├── Collection.ascx │ │ │ │ │ ├── Decimal.ascx │ │ │ │ │ ├── EmailAddress.ascx │ │ │ │ │ ├── HiddenInput.ascx │ │ │ │ │ ├── Html.ascx │ │ │ │ │ ├── Object.ascx │ │ │ │ │ ├── String.ascx │ │ │ │ │ └── Url.ascx │ │ │ │ └── EditorTemplates/ │ │ │ │ ├── Boolean.ascx │ │ │ │ ├── Collection.ascx │ │ │ │ ├── Decimal.ascx │ │ │ │ ├── HiddenInput.ascx │ │ │ │ ├── MultilineText.ascx │ │ │ │ ├── Object.ascx │ │ │ │ ├── Password.ascx │ │ │ │ └── String.ascx │ │ │ ├── iismap.vbs │ │ │ ├── registermvc.wsf │ │ │ └── unregistermvc.wsf │ │ ├── GlobalSuppressions.cs │ │ ├── Html/ │ │ │ └── HtmlHelperExtensions.cs │ │ ├── HtmlButtonType.cs │ │ ├── IMachineKey.cs │ │ ├── ImageExtensions.cs │ │ ├── Internal/ │ │ │ └── ExpressionHelper.cs │ │ ├── LinkBuilder.cs │ │ ├── LinkExtensions.cs │ │ ├── MachineKeyWrapper.cs │ │ ├── MailToExtensions.cs │ │ ├── Microsoft.Web.Mvc.csproj │ │ ├── ModelBinding/ │ │ │ ├── ArrayModelBinder.cs │ │ │ ├── ArrayModelBinderProvider.cs │ │ │ ├── BinaryDataModelBinderProvider.cs │ │ │ ├── BindNeverAttribute.cs │ │ │ ├── BindRequiredAttribute.cs │ │ │ ├── BindingBehavior.cs │ │ │ ├── BindingBehaviorAttribute.cs │ │ │ ├── CollectionModelBinder.cs │ │ │ ├── CollectionModelBinderProvider.cs │ │ │ ├── CollectionModelBinderUtil.cs │ │ │ ├── ComplexModelDto.cs │ │ │ ├── ComplexModelDtoModelBinder.cs │ │ │ ├── ComplexModelDtoModelBinderProvider.cs │ │ │ ├── ComplexModelDtoResult.cs │ │ │ ├── DictionaryModelBinder.cs │ │ │ ├── DictionaryModelBinderProvider.cs │ │ │ ├── ExtensibleModelBinderAdapter.cs │ │ │ ├── ExtensibleModelBinderAttribute.cs │ │ │ ├── ExtensibleModelBindingContext.cs │ │ │ ├── GenericModelBinderProvider.cs │ │ │ ├── IExtensibleModelBinder.cs │ │ │ ├── KeyValuePairModelBinder.cs │ │ │ ├── KeyValuePairModelBinderProvider.cs │ │ │ ├── KeyValuePairModelBinderUtil.cs │ │ │ ├── ModelBinderConfig.cs │ │ │ ├── ModelBinderErrorMessageProvider.cs │ │ │ ├── ModelBinderProvider.cs │ │ │ ├── ModelBinderProviderCollection.cs │ │ │ ├── ModelBinderProviderOptionsAttribute.cs │ │ │ ├── ModelBinderProviders.cs │ │ │ ├── ModelBinderUtil.cs │ │ │ ├── ModelValidatedEventArgs.cs │ │ │ ├── ModelValidatingEventArgs.cs │ │ │ ├── ModelValidationNode.cs │ │ │ ├── MutableObjectModelBinder.cs │ │ │ ├── MutableObjectModelBinderProvider.cs │ │ │ ├── SimpleModelBinderProvider.cs │ │ │ ├── TypeConverterModelBinder.cs │ │ │ ├── TypeConverterModelBinderProvider.cs │ │ │ ├── TypeMatchModelBinder.cs │ │ │ └── TypeMatchModelBinderProvider.cs │ │ ├── ModelCopier.cs │ │ ├── MvcSerializer.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── MvcResources.Designer.cs │ │ │ └── MvcResources.resx │ │ ├── RadioExtensions.cs │ │ ├── ReaderWriterCache.cs │ │ ├── Resources/ │ │ │ ├── ActionType.cs │ │ │ ├── AjaxHelperExtensions.cs │ │ │ ├── AtomEntryActionResult.cs │ │ │ ├── AtomFeedActionResult.cs │ │ │ ├── AtomServiceDocumentActionResult.cs │ │ │ ├── DataContractJsonActionResult.cs │ │ │ ├── DataContractXmlActionResult.cs │ │ │ ├── DefaultFormatHelper.cs │ │ │ ├── DefaultFormatManager.cs │ │ │ ├── FormatHelper.cs │ │ │ ├── FormatManager.cs │ │ │ ├── HtmlHelperExtensions.cs │ │ │ ├── HttpRequestBaseExtensions.cs │ │ │ ├── IEnumerableExtensions.cs │ │ │ ├── IRequestFormatHandler.cs │ │ │ ├── IResponseFormatHandler.cs │ │ │ ├── JsonFormatHandler.cs │ │ │ ├── MultiFormatActionResult.cs │ │ │ ├── RequestContextExtensions.cs │ │ │ ├── ResourceControllerFactory.cs │ │ │ ├── ResourceErrorActionResult.cs │ │ │ ├── ResourceModelBinder.cs │ │ │ ├── ResourceRedirectToRouteResult.cs │ │ │ ├── RouteCollectionExtensions.cs │ │ │ ├── UriHelperExtensions.cs │ │ │ ├── WebApiEnabledAttribute.cs │ │ │ └── XmlFormatHandler.cs │ │ ├── ScriptExtensions.cs │ │ ├── SerializationExtensions.cs │ │ ├── ServerVariablesValueProviderFactory.cs │ │ ├── SessionValueProviderFactory.cs │ │ ├── SkipBindingAttribute.cs │ │ ├── TempDataValueProviderFactory.cs │ │ ├── TypeDescriptorHelper.cs │ │ ├── TypeHelpers.cs │ │ ├── UrlAttribute.cs │ │ ├── ValueProviderUtil.cs │ │ └── ViewExtensions.cs │ ├── Microsoft.Web.WebPages.OAuth/ │ │ ├── AuthenticationClientData.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Microsoft.Web.WebPages.OAuth.csproj │ │ ├── OAuthAccount.cs │ │ ├── OAuthWebSecurity.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── WebResources.Designer.cs │ │ │ └── WebResources.resx │ │ ├── ProviderUserIdSerializationHelper.cs │ │ ├── Resources/ │ │ │ ├── OAuthResources.Designer.cs │ │ │ └── OAuthResources.resx │ │ ├── WebPagesOAuthDataProvider.cs │ │ ├── WebPagesOAuthTokenManager.cs │ │ └── packages.config │ ├── Settings.StyleCop │ ├── Strict.ruleset │ ├── System.Net.Http.Formatting/ │ │ ├── ByteRangeStreamContent.cs │ │ ├── CloneableExtensions.cs │ │ ├── Formatting/ │ │ │ ├── BaseJsonMediaTypeFormatter.cs │ │ │ ├── BsonMediaTypeFormatter.cs │ │ │ ├── BufferedMediaTypeFormatter.cs │ │ │ ├── ContentNegotiationResult.cs │ │ │ ├── DefaultContentNegotiator.cs │ │ │ ├── DelegatingEnumerable.cs │ │ │ ├── FormDataCollection.cs │ │ │ ├── FormUrlEncodedJson.cs │ │ │ ├── FormUrlEncodedMediaTypeFormatter.cs │ │ │ ├── IContentNegotiator.cs │ │ │ ├── IFormatterLogger.cs │ │ │ ├── IRequiredMemberSelector.cs │ │ │ ├── JsonContractResolver.cs │ │ │ ├── JsonMediaTypeFormatter.cs │ │ │ ├── MediaTypeConstants.cs │ │ │ ├── MediaTypeFormatter.cs │ │ │ ├── MediaTypeFormatterCollection.cs │ │ │ ├── MediaTypeFormatterExtensions.cs │ │ │ ├── MediaTypeFormatterMatch.cs │ │ │ ├── MediaTypeFormatterMatchRanking.cs │ │ │ ├── MediaTypeHeaderValueExtensions.cs │ │ │ ├── MediaTypeHeaderValueRange.cs │ │ │ ├── MediaTypeMapping.cs │ │ │ ├── MediaTypeWithQualityHeaderValueComparer.cs │ │ │ ├── ParsedMediaTypeHeaderValue.cs │ │ │ ├── Parsers/ │ │ │ │ ├── FormUrlEncodedParser.cs │ │ │ │ ├── HttpRequestHeaderParser.cs │ │ │ │ ├── HttpRequestLineParser.cs │ │ │ │ ├── HttpResponseHeaderParser.cs │ │ │ │ ├── HttpStatusLineParser.cs │ │ │ │ ├── InternetMessageFormatHeaderParser.cs │ │ │ │ ├── MimeMultipartBodyPartParser.cs │ │ │ │ ├── MimeMultipartParser.cs │ │ │ │ └── ParserState.cs │ │ │ ├── QueryStringMapping.cs │ │ │ ├── RequestHeaderMapping.cs │ │ │ ├── StringComparisonHelper.cs │ │ │ ├── StringWithQualityHeaderValueComparer.cs │ │ │ ├── XmlHttpRequestHeaderMapping.cs │ │ │ └── XmlMediaTypeFormatter.cs │ │ ├── FormattingUtilities.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Handlers/ │ │ │ ├── HttpProgressEventArgs.cs │ │ │ ├── ProgressContent.cs │ │ │ ├── ProgressMessageHandler.cs │ │ │ ├── ProgressStream.cs │ │ │ └── ProgressWriteAsyncResult.cs │ │ ├── Headers/ │ │ │ ├── CookieHeaderValue.cs │ │ │ └── CookieState.cs │ │ ├── HttpClientExtensions.cs │ │ ├── HttpClientFactory.cs │ │ ├── HttpContentExtensions.cs │ │ ├── HttpContentFormDataExtensions.cs │ │ ├── HttpContentMessageExtensions.cs │ │ ├── HttpContentMultipartExtensions.cs │ │ ├── HttpHeaderExtensions.cs │ │ ├── HttpMessageContent.cs │ │ ├── HttpRequestHeadersExtensions.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── HttpResponseHeadersExtensions.cs │ │ ├── HttpUnsortedHeaders.cs │ │ ├── HttpUnsortedRequest.cs │ │ ├── HttpUnsortedResponse.cs │ │ ├── Internal/ │ │ │ ├── AsyncResult.cs │ │ │ ├── ByteRangeStream.cs │ │ │ ├── DelegatingStream.cs │ │ │ ├── HttpValueCollection.cs │ │ │ ├── NonClosingDelegatingStream.cs │ │ │ ├── NullableAttributes.cs │ │ │ ├── TranscodingStream.cs │ │ │ └── TypeExtensions.cs │ │ ├── InvalidByteRangeException.cs │ │ ├── MimeBodyPart.cs │ │ ├── MultipartFileData.cs │ │ ├── MultipartFileStreamProvider.cs │ │ ├── MultipartFormDataRemoteStreamProvider.cs │ │ ├── MultipartFormDataStreamProvider.cs │ │ ├── MultipartFormDataStreamProviderHelper.cs │ │ ├── MultipartMemoryStreamProvider.cs │ │ ├── MultipartRelatedStreamProvider.cs │ │ ├── MultipartRemoteFileData.cs │ │ ├── MultipartStreamProvider.cs │ │ ├── ObjectContent.cs │ │ ├── ObjectContentOfT.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── PushStreamContent.cs │ │ ├── RemoteStreamInfo.cs │ │ ├── System.Net.Http.Formatting.csproj │ │ ├── UnsupportedMediaTypeException.cs │ │ ├── UriExtensions.cs │ │ └── packages.config │ ├── System.Net.Http.Formatting.ns1_3/ │ │ ├── ICloneable.cs │ │ ├── MediaTypeHeaderValueExtensions.cs │ │ └── System.Net.Http.Formatting.ns1_3.csproj │ ├── System.Net.Http.Formatting.ns2_0/ │ │ └── System.Net.Http.Formatting.ns2_0.csproj │ ├── System.Web.Cors/ │ │ ├── CorsConstants.cs │ │ ├── CorsEngine.cs │ │ ├── CorsPolicy.cs │ │ ├── CorsRequestContext.cs │ │ ├── CorsResult.cs │ │ ├── ICorsEngine.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ └── System.Web.Cors.csproj │ ├── System.Web.Helpers/ │ │ ├── Chart/ │ │ │ ├── Chart.cs │ │ │ └── ChartTheme.cs │ │ ├── Common/ │ │ │ └── VirtualPathUtil.cs │ │ ├── ConversionUtil.cs │ │ ├── Crypto.cs │ │ ├── DynamicHelper.cs │ │ ├── DynamicJavaScriptConverter.cs │ │ ├── DynamicJsonArray.cs │ │ ├── DynamicJsonObject.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HtmlElement.cs │ │ ├── HtmlObjectPrinter.cs │ │ ├── Json.cs │ │ ├── ObjectInfo.cs │ │ ├── ObjectVisitor.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ ├── ChartTemplates.Designer.cs │ │ │ ├── ChartTemplates.resx │ │ │ ├── HelpersResources.Designer.cs │ │ │ └── HelpersResources.resx │ │ ├── ServerInfo.cs │ │ ├── SortDirection.cs │ │ ├── System.Web.Helpers.csproj │ │ ├── WebCache.cs │ │ ├── WebGrid/ │ │ │ ├── IWebGridDataSource.cs │ │ │ ├── PreComputedGridDataSource.cs │ │ │ ├── SortInfo.cs │ │ │ ├── WebGrid.cs │ │ │ ├── WebGridColumn.cs │ │ │ ├── WebGridDataSource.cs │ │ │ ├── WebGridPagerModes.cs │ │ │ ├── WebGridRow.cs │ │ │ ├── _WebGridRenderer.cshtml │ │ │ └── _WebGridRenderer.generated.cs │ │ ├── WebImage.cs │ │ └── WebMail.cs │ ├── System.Web.Http/ │ │ ├── AcceptVerbsAttribute.cs │ │ ├── ActionNameAttribute.cs │ │ ├── AllowAnonymousAttribute.cs │ │ ├── ApiController.cs │ │ ├── AuthorizeAttribute.cs │ │ ├── Batch/ │ │ │ ├── BatchExecutionOrder.cs │ │ │ ├── BatchHttpRequestContext.cs │ │ │ ├── BatchHttpRequestMessageExtensions.cs │ │ │ ├── DefaultHttpBatchHandler.cs │ │ │ └── HttpBatchHandler.cs │ │ ├── Controllers/ │ │ │ ├── ActionFilterResult.cs │ │ │ ├── ApiControllerActionInvoker.cs │ │ │ ├── ApiControllerActionSelector.cs │ │ │ ├── AuthenticationFilterResult.cs │ │ │ ├── AuthorizationFilterResult.cs │ │ │ ├── CandidateAction.cs │ │ │ ├── ExceptionFilterResult.cs │ │ │ ├── FilterGrouping.cs │ │ │ ├── HttpActionBinding.cs │ │ │ ├── HttpActionContext.cs │ │ │ ├── HttpActionContextExtensions.cs │ │ │ ├── HttpActionDescriptor.cs │ │ │ ├── HttpActionDescriptorExtensions.cs │ │ │ ├── HttpControllerContext.cs │ │ │ ├── HttpControllerDescriptor.cs │ │ │ ├── HttpControllerDescriptorExtensions.cs │ │ │ ├── HttpControllerSettings.cs │ │ │ ├── HttpParameterBinding.cs │ │ │ ├── HttpParameterDescriptor.cs │ │ │ ├── HttpParameterDescriptorExtensions.cs │ │ │ ├── HttpRequestContext.cs │ │ │ ├── IActionHttpMethodProvider.cs │ │ │ ├── IActionResultConverter.cs │ │ │ ├── IActionValueBinder.cs │ │ │ ├── IControllerConfiguration.cs │ │ │ ├── IHttpActionInvoker.cs │ │ │ ├── IHttpActionSelector.cs │ │ │ ├── IHttpController.cs │ │ │ ├── ReflectedHttpActionDescriptor.cs │ │ │ ├── ReflectedHttpParameterDescriptor.cs │ │ │ ├── RequestBackedHttpRequestContext.cs │ │ │ ├── ResponseMessageResultConverter.cs │ │ │ ├── ValueResultConverter.cs │ │ │ └── VoidResultConverter.cs │ │ ├── Dependencies/ │ │ │ ├── EmptyResolver.cs │ │ │ ├── IDependencyResolver.cs │ │ │ └── IDependencyScope.cs │ │ ├── Description/ │ │ │ ├── ApiDescription.cs │ │ │ ├── ApiExplorer.cs │ │ │ ├── ApiExplorerSettingsAttribute.cs │ │ │ ├── ApiParameterDescription.cs │ │ │ ├── ApiParameterSource.cs │ │ │ ├── IApiExplorer.cs │ │ │ ├── IDocumentationProvider.cs │ │ │ ├── ResponseDescription.cs │ │ │ └── ResponseTypeAttribute.cs │ │ ├── Dispatcher/ │ │ │ ├── DefaultAssembliesResolver.cs │ │ │ ├── DefaultHttpControllerActivator.cs │ │ │ ├── DefaultHttpControllerSelector.cs │ │ │ ├── DefaultHttpControllerTypeResolver.cs │ │ │ ├── HttpControllerDispatcher.cs │ │ │ ├── HttpControllerTypeCache.cs │ │ │ ├── HttpRoutingDispatcher.cs │ │ │ ├── IAssembliesResolver.cs │ │ │ ├── IHttpControllerActivator.cs │ │ │ ├── IHttpControllerSelector.cs │ │ │ └── IHttpControllerTypeResolver.cs │ │ ├── EmptyReadOnlyDictionary.cs │ │ ├── ExceptionHandling/ │ │ │ ├── CompositeExceptionLogger.cs │ │ │ ├── DefaultExceptionHandler.cs │ │ │ ├── EmptyExceptionHandler.cs │ │ │ ├── ExceptionCatchBlocks.cs │ │ │ ├── ExceptionContext.cs │ │ │ ├── ExceptionContextCatchBlock.cs │ │ │ ├── ExceptionHandler.cs │ │ │ ├── ExceptionHandlerContext.cs │ │ │ ├── ExceptionHandlerExtensions.cs │ │ │ ├── ExceptionLogger.cs │ │ │ ├── ExceptionLoggerContext.cs │ │ │ ├── ExceptionLoggerExtensions.cs │ │ │ ├── ExceptionServices.cs │ │ │ ├── IExceptionHandler.cs │ │ │ ├── IExceptionLogger.cs │ │ │ └── LastChanceExceptionHandler.cs │ │ ├── Filters/ │ │ │ ├── ActionDescriptorFilterProvider.cs │ │ │ ├── ActionFilterAttribute.cs │ │ │ ├── AuthorizationFilterAttribute.cs │ │ │ ├── ConfigurationFilterProvider.cs │ │ │ ├── ExceptionFilterAttribute.cs │ │ │ ├── FilterAttribute.cs │ │ │ ├── FilterInfo.cs │ │ │ ├── FilterInfoComparer.cs │ │ │ ├── FilterScope.cs │ │ │ ├── HttpActionExecutedContext.cs │ │ │ ├── HttpAuthenticationChallengeContext.cs │ │ │ ├── HttpAuthenticationContext.cs │ │ │ ├── HttpFilterCollection.cs │ │ │ ├── IActionFilter.cs │ │ │ ├── IAuthenticationFilter.cs │ │ │ ├── IAuthorizationFilter.cs │ │ │ ├── IExceptionFilter.cs │ │ │ ├── IFilter.cs │ │ │ ├── IFilterProvider.cs │ │ │ └── IOverrideFilter.cs │ │ ├── FromBodyAttribute.cs │ │ ├── FromUriAttribute.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Hosting/ │ │ │ ├── HttpPropertyKeys.cs │ │ │ ├── IHostBufferPolicySelector.cs │ │ │ └── SuppressHostPrincipalMessageHandler.cs │ │ ├── HttpBindNeverAttribute.cs │ │ ├── HttpBindRequiredAttribute.cs │ │ ├── HttpConfiguration.cs │ │ ├── HttpConfigurationExtensions.cs │ │ ├── HttpDeleteAttribute.cs │ │ ├── HttpError.cs │ │ ├── HttpErrorKeys.cs │ │ ├── HttpGetAttribute.cs │ │ ├── HttpHeadAttribute.cs │ │ ├── HttpOptionsAttribute.cs │ │ ├── HttpPatchAttribute.cs │ │ ├── HttpPostAttribute.cs │ │ ├── HttpPutAttribute.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── HttpResponseException.cs │ │ ├── HttpResponseMessageExtensions.cs │ │ ├── HttpRouteCollection.cs │ │ ├── HttpRouteCollectionExtensions.cs │ │ ├── HttpServer.cs │ │ ├── IHttpActionResult.cs │ │ ├── IncludeErrorDetailPolicy.cs │ │ ├── Internal/ │ │ │ ├── CollectionModelBinderUtil.cs │ │ │ ├── HttpParameterBindingExtensions.cs │ │ │ ├── MemberInfoExtensions.cs │ │ │ ├── ParameterInfoExtensions.cs │ │ │ ├── TypeActivator.cs │ │ │ ├── TypeDescriptorHelper.cs │ │ │ └── TypeHelper.cs │ │ ├── Metadata/ │ │ │ ├── ModelMetadata.cs │ │ │ ├── ModelMetadataProvider.cs │ │ │ └── Providers/ │ │ │ ├── AssociatedMetadataProvider.cs │ │ │ ├── CachedDataAnnotationsMetadataAttributes.cs │ │ │ ├── CachedDataAnnotationsModelMetadata.cs │ │ │ ├── CachedModelMetadata.cs │ │ │ ├── DataAnnotationsModelMetadataProvider.cs │ │ │ └── EmptyMetadataProvider.cs │ │ ├── ModelBinding/ │ │ │ ├── Binders/ │ │ │ │ ├── ArrayModelBinder.cs │ │ │ │ ├── ArrayModelBinderProvider.cs │ │ │ │ ├── CollectionModelBinder.cs │ │ │ │ ├── CollectionModelBinderProvider.cs │ │ │ │ ├── ComplexModelDto.cs │ │ │ │ ├── ComplexModelDtoModelBinder.cs │ │ │ │ ├── ComplexModelDtoModelBinderProvider.cs │ │ │ │ ├── ComplexModelDtoResult.cs │ │ │ │ ├── CompositeModelBinder.cs │ │ │ │ ├── CompositeModelBinderProvider.cs │ │ │ │ ├── DictionaryModelBinder.cs │ │ │ │ ├── DictionaryModelBinderProvider.cs │ │ │ │ ├── KeyValuePairModelBinder.cs │ │ │ │ ├── KeyValuePairModelBinderProvider.cs │ │ │ │ ├── MutableObjectModelBinder.cs │ │ │ │ ├── MutableObjectModelBinderProvider.cs │ │ │ │ ├── SimpleModelBinderProvider.cs │ │ │ │ ├── TypeConverterModelBinder.cs │ │ │ │ ├── TypeConverterModelBinderProvider.cs │ │ │ │ ├── TypeMatchModelBinder.cs │ │ │ │ └── TypeMatchModelBinderProvider.cs │ │ │ ├── CancellationTokenParameterBinding.cs │ │ │ ├── CustomModelBinderAttribute.cs │ │ │ ├── DefaultActionValueBinder.cs │ │ │ ├── ErrorParameterBinding.cs │ │ │ ├── FormDataCollectionExtensions.cs │ │ │ ├── FormatterParameterBinding.cs │ │ │ ├── HttpBindingBehavior.cs │ │ │ ├── HttpBindingBehaviorAttribute.cs │ │ │ ├── HttpRequestParameterBinding.cs │ │ │ ├── IModelBinder.cs │ │ │ ├── IValueProviderParameterBinding.cs │ │ │ ├── JQueryMVCFormUrlEncodedFormatter.cs │ │ │ ├── ModelBinderAttribute.cs │ │ │ ├── ModelBinderConfig.cs │ │ │ ├── ModelBinderErrorMessageProvider.cs │ │ │ ├── ModelBinderParameterBinding.cs │ │ │ ├── ModelBinderProvider.cs │ │ │ ├── ModelBindingContext.cs │ │ │ ├── ModelBindingHelper.cs │ │ │ ├── ModelError.cs │ │ │ ├── ModelErrorCollection.cs │ │ │ ├── ModelState.cs │ │ │ ├── ModelStateDictionary.cs │ │ │ └── ParameterBindingRulesCollection.cs │ │ ├── NonActionAttribute.cs │ │ ├── OverrideActionFiltersAttribute.cs │ │ ├── OverrideAuthenticationAttribute.cs │ │ ├── OverrideAuthorizationAttribute.cs │ │ ├── OverrideExceptionFiltersAttribute.cs │ │ ├── ParameterBindingAttribute.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── Results/ │ │ │ ├── BadRequestErrorMessageResult.cs │ │ │ ├── BadRequestResult.cs │ │ │ ├── ConflictResult.cs │ │ │ ├── CreatedAtRouteNegotiatedContentResult.cs │ │ │ ├── CreatedNegotiatedContentResult.cs │ │ │ ├── ExceptionResult.cs │ │ │ ├── FormattedContentResult.cs │ │ │ ├── InternalServerErrorResult.cs │ │ │ ├── InvalidModelStateResult.cs │ │ │ ├── JsonResult.cs │ │ │ ├── NegotiatedContentResult.cs │ │ │ ├── NotFoundResult.cs │ │ │ ├── OkNegotiatedContentResult.cs │ │ │ ├── OkResult.cs │ │ │ ├── RedirectResult.cs │ │ │ ├── RedirectToRouteResult.cs │ │ │ ├── ResponseMessageResult.cs │ │ │ ├── StatusCodeResult.cs │ │ │ └── UnauthorizedResult.cs │ │ ├── RouteAttribute.cs │ │ ├── RouteParameter.cs │ │ ├── RoutePrefixAttribute.cs │ │ ├── Routing/ │ │ │ ├── AttributeRoutingMapper.cs │ │ │ ├── BoundRouteTemplate.cs │ │ │ ├── DefaultDirectRouteProvider.cs │ │ │ ├── HttpMethodConstraint.cs │ │ │ ├── HttpParsedRoute.cs │ │ │ ├── HttpRoute.cs │ │ │ ├── HttpRouteData.cs │ │ │ ├── HttpRouteDataExtensions.cs │ │ │ ├── HttpRouteDirection.cs │ │ │ ├── HttpRouteExtensions.cs │ │ │ ├── HttpRouteValueDictionary.cs │ │ │ ├── HttpVirtualPathData.cs │ │ │ ├── IHttpRoute.cs │ │ │ ├── IHttpRouteConstraint.cs │ │ │ ├── IHttpRouteData.cs │ │ │ ├── IHttpRouteInfoProvider.cs │ │ │ ├── IHttpVirtualPathData.cs │ │ │ ├── LinkGenerationRoute.cs │ │ │ ├── MediaTypeFormatterExtensions.cs │ │ │ ├── RouteCollectionRoute.cs │ │ │ ├── RouteDataTokenKeys.cs │ │ │ ├── RouteValueKeys.cs │ │ │ ├── RoutingContext.cs │ │ │ ├── StopRoutingHandler.cs │ │ │ ├── UriPathExtensionMapping.cs │ │ │ └── UrlHelper.cs │ │ ├── Services/ │ │ │ ├── ControllerServices.cs │ │ │ ├── Decorator.cs │ │ │ ├── DefaultServices.cs │ │ │ ├── IDecorator.cs │ │ │ └── ServicesContainer.cs │ │ ├── ServicesExtensions.cs │ │ ├── SingleResult.cs │ │ ├── SingleResultOfT.cs │ │ ├── System.Web.Http.csproj │ │ ├── Tracing/ │ │ │ ├── FormattingUtilities.cs │ │ │ ├── IFormatterTracer.cs │ │ │ ├── ITraceManager.cs │ │ │ ├── ITraceWriter.cs │ │ │ ├── ITraceWriterExtensions.cs │ │ │ ├── TraceCategories.cs │ │ │ ├── TraceKind.cs │ │ │ ├── TraceKindHelper.cs │ │ │ ├── TraceLevel.cs │ │ │ ├── TraceLevelHelper.cs │ │ │ ├── TraceManager.cs │ │ │ ├── TraceRecord.cs │ │ │ └── Tracers/ │ │ │ ├── ActionFilterAttributeTracer.cs │ │ │ ├── ActionFilterTracer.cs │ │ │ ├── ActionValueBinderTracer.cs │ │ │ ├── AuthenticationFilterTracer.cs │ │ │ ├── AuthorizationFilterAttributeTracer.cs │ │ │ ├── AuthorizationFilterTracer.cs │ │ │ ├── BufferedMediaTypeFormatterTracer.cs │ │ │ ├── ContentNegotiatorTracer.cs │ │ │ ├── DefaultHttpControllerTypeResolverTracer.cs │ │ │ ├── ExceptionFilterAttributeTracer.cs │ │ │ ├── ExceptionFilterTracer.cs │ │ │ ├── FilterTracer.cs │ │ │ ├── FormUrlEncodedMediaTypeFormatterTracer.cs │ │ │ ├── FormatterLoggerTraceWrapper.cs │ │ │ ├── FormatterParameterBindingTracer.cs │ │ │ ├── HttpActionBindingTracer.cs │ │ │ ├── HttpActionDescriptorTracer.cs │ │ │ ├── HttpActionInvokerTracer.cs │ │ │ ├── HttpActionSelectorTracer.cs │ │ │ ├── HttpControllerActivatorTracer.cs │ │ │ ├── HttpControllerDescriptorTracer.cs │ │ │ ├── HttpControllerSelectorTracer.cs │ │ │ ├── HttpControllerTracer.cs │ │ │ ├── HttpParameterBindingTracer.cs │ │ │ ├── JsonMediaTypeFormatterTracer.cs │ │ │ ├── MediaTypeFormatterTracer.cs │ │ │ ├── MessageHandlerTracer.cs │ │ │ ├── OverrideFilterTracer.cs │ │ │ ├── RequestMessageHandlerTracer.cs │ │ │ └── XmlMediaTypeFormatterTracer.cs │ │ ├── Validation/ │ │ │ ├── BodyModelValidatorContext.cs │ │ │ ├── DefaultBodyModelValidator.cs │ │ │ ├── IBodyModelValidator.cs │ │ │ ├── IBodyModelValidatorKeyBuilder.cs │ │ │ ├── IModelValidatorCache.cs │ │ │ ├── ModelStateFormatterLogger.cs │ │ │ ├── ModelValidatedEventArgs.cs │ │ │ ├── ModelValidatingEventArgs.cs │ │ │ ├── ModelValidationNode.cs │ │ │ ├── ModelValidationRequiredMemberSelector.cs │ │ │ ├── ModelValidationResult.cs │ │ │ ├── ModelValidator.cs │ │ │ ├── ModelValidatorCache.cs │ │ │ ├── ModelValidatorProvider.cs │ │ │ ├── Providers/ │ │ │ │ ├── AssociatedValidatorProvider.cs │ │ │ │ ├── DataAnnotationsModelValidatorProvider.cs │ │ │ │ ├── DataMemberModelValidatorProvider.cs │ │ │ │ ├── InvalidModelValidatorProvider.cs │ │ │ │ └── RequiredMemberModelValidatorProvider.cs │ │ │ ├── ReferenceEqualityComparer.cs │ │ │ └── Validators/ │ │ │ ├── DataAnnotationsModelValidator.cs │ │ │ ├── ErrorModelValidator.cs │ │ │ ├── RequiredMemberModelValidator.cs │ │ │ └── ValidatableObjectAdapter.cs │ │ ├── ValueProviders/ │ │ │ ├── IEnumerableValueProvider.cs │ │ │ ├── IUriValueProviderFactory.cs │ │ │ ├── IValueProvider.cs │ │ │ ├── Providers/ │ │ │ │ ├── CompositeValueProvider.cs │ │ │ │ ├── CompositeValueProviderFactory.cs │ │ │ │ ├── ElementalValueProvider.cs │ │ │ │ ├── NameValuePairsValueProvider.cs │ │ │ │ ├── QueryStringValueProvider.cs │ │ │ │ ├── QueryStringValueProviderFactory.cs │ │ │ │ ├── RouteDataValueProvider.cs │ │ │ │ └── RouteDataValueProviderFactory.cs │ │ │ ├── ValueProviderAttribute.cs │ │ │ ├── ValueProviderFactory.cs │ │ │ └── ValueProviderResult.cs │ │ └── packages.config │ ├── System.Web.Http.Cors/ │ │ ├── AttributeBasedPolicyProviderFactory.cs │ │ ├── CorsHttpConfigurationExtensions.cs │ │ ├── CorsHttpRequestMessageExtensions.cs │ │ ├── CorsHttpResponseMessageExtensions.cs │ │ ├── CorsMessageHandler.cs │ │ ├── DisableCorsAttribute.cs │ │ ├── EnableCorsAttribute.cs │ │ ├── ICorsPolicyProvider.cs │ │ ├── ICorsPolicyProviderFactory.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── System.Web.Http.Cors.csproj │ │ └── Tracing/ │ │ ├── CorsEngineTracer.cs │ │ ├── CorsPolicyProviderFactoryTracer.cs │ │ ├── CorsPolicyProviderTracer.cs │ │ └── TraceCategories.cs │ ├── System.Web.Http.Owin/ │ │ ├── ExceptionHandling/ │ │ │ ├── DefaultExceptionHandler.cs │ │ │ └── EmptyExceptionLogger.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HostAuthenticationAttribute.cs │ │ ├── HostAuthenticationFilter.cs │ │ ├── HttpMessageHandlerAdapter.cs │ │ ├── HttpMessageHandlerOptions.cs │ │ ├── OwinBufferPolicySelector.cs │ │ ├── OwinConstants.cs │ │ ├── OwinExceptionCatchBlocks.cs │ │ ├── OwinHttpConfigurationExtensions.cs │ │ ├── OwinHttpRequestContext.cs │ │ ├── OwinHttpRequestMessageExtensions.cs │ │ ├── OwinRequestExtensions.cs │ │ ├── OwinResponseExtensions.cs │ │ ├── PassiveAuthenticationMessageHandler.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── OwinResources.Designer.cs │ │ │ └── OwinResources.resx │ │ ├── System.Web.Http.Owin.csproj │ │ ├── WebApiAppBuilderExtensions.cs │ │ └── packages.config │ ├── System.Web.Http.SelfHost/ │ │ ├── Channels/ │ │ │ ├── HttpBinding.cs │ │ │ ├── HttpBindingSecurity.cs │ │ │ ├── HttpBindingSecurityMode.cs │ │ │ ├── HttpBindingSecurityModeHelper.cs │ │ │ ├── HttpMessage.cs │ │ │ ├── HttpMessageEncoderFactory.cs │ │ │ ├── HttpMessageEncodingBindingElement.cs │ │ │ ├── HttpMessageEncodingChannelListener.cs │ │ │ ├── HttpMessageEncodingReplyChannel.cs │ │ │ ├── HttpMessageEncodingRequestContext.cs │ │ │ └── HttpMessageExtensions.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── HttpSelfHostConfiguration.cs │ │ ├── HttpSelfHostServer.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── SelfHostHttpRequestContext.cs │ │ ├── ServiceModel/ │ │ │ ├── Channels/ │ │ │ │ ├── AsyncResult.cs │ │ │ │ ├── BufferManagerOutputStream.cs │ │ │ │ ├── BufferedOutputStream.cs │ │ │ │ ├── ChannelAcceptor.cs │ │ │ │ ├── ChannelBindingUtility.cs │ │ │ │ ├── CompletedAsyncResult.cs │ │ │ │ ├── CompletedAsyncResultOfT.cs │ │ │ │ ├── HttpTransportDefaults.cs │ │ │ │ ├── IChannelAcceptor.cs │ │ │ │ ├── LayeredChannel.cs │ │ │ │ ├── LayeredChannelAcceptor.cs │ │ │ │ ├── LayeredChannelListener.cs │ │ │ │ └── TransportDefaults.cs │ │ │ ├── HostNameComparisonModeHelper.cs │ │ │ ├── HttpClientCredentialTypeHelper.cs │ │ │ ├── HttpProxyCredentialTypeHelper.cs │ │ │ ├── HttpTransportSecurityExtensionMethods.cs │ │ │ └── TransferModeHelper.cs │ │ └── System.Web.Http.SelfHost.csproj │ ├── System.Web.Http.SignalR/ │ │ ├── GlobalSuppressions.cs │ │ ├── HubController.cs │ │ ├── HubControllerBase.cs │ │ ├── HubControllerOfTHub.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── System.Web.Http.SignalR.csproj │ │ └── packages.config │ ├── System.Web.Http.Tracing/ │ │ ├── GlobalSuppressions.cs │ │ ├── HttpConfigurationExtensions.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── System.Web.Http.Tracing.csproj │ │ ├── SystemDiagnosticsTraceWriter.cs │ │ └── packages.config │ ├── System.Web.Http.WebHost/ │ │ ├── GlobalConfiguration.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HttpBatchContextWrapper.cs │ │ ├── HttpControllerHandler.cs │ │ ├── HttpControllerRouteHandler.cs │ │ ├── HttpControllerTypeCacheSerializer.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── HttpResponseBaseExtensions.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── SRResources.Designer.cs │ │ │ └── SRResources.resx │ │ ├── RouteCollectionExtensions.cs │ │ ├── Routing/ │ │ │ ├── HostedHttpRoute.cs │ │ │ ├── HostedHttpRouteCollection.cs │ │ │ ├── HostedHttpRouteData.cs │ │ │ ├── HostedHttpVirtualPathData.cs │ │ │ ├── HttpContextBaseExtensions.cs │ │ │ ├── HttpRequestMessageContextWrapper.cs │ │ │ ├── HttpRequestMessageWrapper.cs │ │ │ ├── HttpRouteDataExtensions.cs │ │ │ ├── HttpRouteExceptionHandler.cs │ │ │ ├── HttpRouteExceptionRouteHandler.cs │ │ │ ├── HttpRouteExtensions.cs │ │ │ └── HttpWebRoute.cs │ │ ├── SeekableBufferedRequestStream.cs │ │ ├── SuppressFormsAuthRedirectHelper.cs │ │ ├── System.Web.Http.WebHost.csproj │ │ ├── TaskWrapperAsyncResult.cs │ │ ├── WebHostAssembliesResolver.cs │ │ ├── WebHostBufferPolicySelector.cs │ │ ├── WebHostExceptionCatchBlocks.cs │ │ ├── WebHostExceptionHandler.cs │ │ ├── WebHostHttpControllerTypeResolver.cs │ │ ├── WebHostHttpRequestContext.cs │ │ └── packages.config │ ├── System.Web.Mvc/ │ │ ├── AcceptVerbsAttribute.cs │ │ ├── ActionDescriptor.cs │ │ ├── ActionDescriptorHelper.cs │ │ ├── ActionExecutedContext.cs │ │ ├── ActionExecutingContext.cs │ │ ├── ActionFilterAttribute.cs │ │ ├── ActionMethodDispatcher.cs │ │ ├── ActionMethodDispatcherCache.cs │ │ ├── ActionMethodSelector.cs │ │ ├── ActionMethodSelectorAttribute.cs │ │ ├── ActionMethodSelectorBase.cs │ │ ├── ActionNameAttribute.cs │ │ ├── ActionNameSelector.cs │ │ ├── ActionNameSelectorAttribute.cs │ │ ├── ActionResult.cs │ │ ├── ActionSelector.cs │ │ ├── AdditionalMetaDataAttribute.cs │ │ ├── Ajax/ │ │ │ ├── AjaxExtensions.cs │ │ │ ├── AjaxOptions.cs │ │ │ └── InsertionMode.cs │ │ ├── AjaxHelper.cs │ │ ├── AjaxHelperOfTModel.cs │ │ ├── AjaxRequestExtensions.cs │ │ ├── AllowAnonymousAttribute.cs │ │ ├── AllowHtmlAttribute.cs │ │ ├── AreaHelpers.cs │ │ ├── AreaReference.cs │ │ ├── AreaRegistration.cs │ │ ├── AreaRegistrationContext.cs │ │ ├── AssociatedMetadataProvider.cs │ │ ├── AssociatedValidatorProvider.cs │ │ ├── Async/ │ │ │ ├── ActionDescriptorCreator.cs │ │ │ ├── AsyncActionDescriptor.cs │ │ │ ├── AsyncActionMethodSelector.cs │ │ │ ├── AsyncControllerActionInvoker.cs │ │ │ ├── AsyncManager.cs │ │ │ ├── AsyncResultWrapper.cs │ │ │ ├── AsyncVoid.cs │ │ │ ├── BeginInvokeDelegate.cs │ │ │ ├── BeginInvokeDelegateOfTState.cs │ │ │ ├── EndInvokeDelegateOfTResult.cs │ │ │ ├── EndInvokeDelegateOfTResultTState.cs │ │ │ ├── EndInvokeVoidDelegate.cs │ │ │ ├── IAsyncActionInvoker.cs │ │ │ ├── IAsyncActionInvokerFactory.cs │ │ │ ├── IAsyncController.cs │ │ │ ├── IAsyncManagerContainer.cs │ │ │ ├── OperationCounter.cs │ │ │ ├── ReflectedAsyncActionDescriptor.cs │ │ │ ├── ReflectedAsyncControllerDescriptor.cs │ │ │ ├── SimpleAsyncResult.cs │ │ │ ├── SingleEntryGate.cs │ │ │ ├── SynchronizationContextUtil.cs │ │ │ ├── SynchronousOperationException.cs │ │ │ ├── TaskAsyncActionDescriptor.cs │ │ │ ├── TaskWrapperAsyncResult.cs │ │ │ ├── Trigger.cs │ │ │ └── TriggerListener.cs │ │ ├── AsyncController.cs │ │ ├── AsyncTimeoutAttribute.cs │ │ ├── AuthorizationContext.cs │ │ ├── AuthorizeAttribute.cs │ │ ├── BindAttribute.cs │ │ ├── BuildManagerCompiledView.cs │ │ ├── BuildManagerViewEngine.cs │ │ ├── BuildManagerWrapper.cs │ │ ├── ByteArrayModelBinder.cs │ │ ├── CachedAssociatedMetadataProvider.cs │ │ ├── CachedDataAnnotationsMetadataAttributes.cs │ │ ├── CachedDataAnnotationsModelMetadata.cs │ │ ├── CachedDataAnnotationsModelMetadataProvider.cs │ │ ├── CachedModelMetadata.cs │ │ ├── CancellationTokenModelBinder.cs │ │ ├── ChildActionOnlyAttribute.cs │ │ ├── ChildActionValueProvider.cs │ │ ├── ChildActionValueProviderFactory.cs │ │ ├── ClientDataTypeModelValidatorProvider.cs │ │ ├── CompareAttribute.cs │ │ ├── CompareAttributeAdapter.cs │ │ ├── ContentResult.cs │ │ ├── Controller.cs │ │ ├── ControllerActionInvoker.cs │ │ ├── ControllerBase.cs │ │ ├── ControllerBuilder.cs │ │ ├── ControllerContext.cs │ │ ├── ControllerDescriptor.cs │ │ ├── ControllerDescriptorCache.cs │ │ ├── ControllerDescriptorExtensions.cs │ │ ├── ControllerInstanceFilterProvider.cs │ │ ├── ControllerTypeCache.cs │ │ ├── CopyOnWriteDictionary.cs │ │ ├── CustomModelBinderAttribute.cs │ │ ├── DataAnnotationsModelMetadata.cs │ │ ├── DataAnnotationsModelMetadataProvider.cs │ │ ├── DataAnnotationsModelValidator.cs │ │ ├── DataAnnotationsModelValidatorOfTAttribute.cs │ │ ├── DataAnnotationsModelValidatorProvider.cs │ │ ├── DataErrorInfoModelValidatorProvider.cs │ │ ├── DataTypeAttributeAdapter.cs │ │ ├── DataTypeUtil.cs │ │ ├── DefaultControllerFactory.cs │ │ ├── DefaultModelBinder.cs │ │ ├── DefaultViewLocationCache.cs │ │ ├── DependencyResolver.cs │ │ ├── DependencyResolverExtensions.cs │ │ ├── DescriptorUtil.cs │ │ ├── DictionaryHelpers.cs │ │ ├── DictionaryValueProvider.cs │ │ ├── DynamicViewDataDictionary.cs │ │ ├── EmptyModelMetadataProvider.cs │ │ ├── EmptyModelValidatorProvider.cs │ │ ├── EmptyResult.cs │ │ ├── Error.cs │ │ ├── ExceptionContext.cs │ │ ├── ExpressionHelper.cs │ │ ├── ExpressionUtil/ │ │ │ ├── BinaryExpressionFingerprint.cs │ │ │ ├── CachedExpressionCompiler.cs │ │ │ ├── ConditionalExpressionFingerprint.cs │ │ │ ├── ConstantExpressionFingerprint.cs │ │ │ ├── DefaultExpressionFingerprint.cs │ │ │ ├── ExpressionFingerprint.cs │ │ │ ├── ExpressionFingerprintChain.cs │ │ │ ├── FingerprintingExpressionVisitor.cs │ │ │ ├── HashCodeCombiner.cs │ │ │ ├── Hoisted.cs │ │ │ ├── HoistingExpressionVisitor.cs │ │ │ ├── IndexExpressionFingerprint.cs │ │ │ ├── LambdaExpressionFingerprint.cs │ │ │ ├── MemberExpressionFingerprint.cs │ │ │ ├── MethodCallExpressionFingerprint.cs │ │ │ ├── ParameterExpressionFingerprint.cs │ │ │ ├── TypeBinaryExpressionFingerprint.cs │ │ │ └── UnaryExpressionFingerprint.cs │ │ ├── FieldValidationMetadata.cs │ │ ├── FileContentResult.cs │ │ ├── FileExtensionsAttributeAdapter.cs │ │ ├── FilePathResult.cs │ │ ├── FileResult.cs │ │ ├── FileStreamResult.cs │ │ ├── Filter.cs │ │ ├── FilterAttribute.cs │ │ ├── FilterAttributeFilterProvider.cs │ │ ├── FilterInfo.cs │ │ ├── FilterProviderCollection.cs │ │ ├── FilterProviders.cs │ │ ├── FilterScope.cs │ │ ├── Filters/ │ │ │ ├── AuthenticationChallengeContext.cs │ │ │ ├── AuthenticationContext.cs │ │ │ ├── IAuthenticationFilter.cs │ │ │ └── IOverrideFilter.cs │ │ ├── FormCollection.cs │ │ ├── FormContext.cs │ │ ├── FormMethod.cs │ │ ├── FormValueProvider.cs │ │ ├── FormValueProviderFactory.cs │ │ ├── GlobalFilterCollection.cs │ │ ├── GlobalFilters.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HandleErrorAttribute.cs │ │ ├── HandleErrorInfo.cs │ │ ├── HiddenInputAttribute.cs │ │ ├── Html/ │ │ │ ├── ChildActionExtensions.cs │ │ │ ├── DefaultDisplayTemplates.cs │ │ │ ├── DefaultEditorTemplates.cs │ │ │ ├── DisplayExtensions.cs │ │ │ ├── DisplayNameExtensions.cs │ │ │ ├── DisplayTextExtensions.cs │ │ │ ├── EditorExtensions.cs │ │ │ ├── EnumHelper.cs │ │ │ ├── FormExtensions.cs │ │ │ ├── InputExtensions.cs │ │ │ ├── LabelExtensions.cs │ │ │ ├── LinkExtensions.cs │ │ │ ├── MvcForm.cs │ │ │ ├── NameExtensions.cs │ │ │ ├── PartialExtensions.cs │ │ │ ├── RenderPartialExtensions.cs │ │ │ ├── SelectExtensions.cs │ │ │ ├── TemplateHelpers.cs │ │ │ ├── TextAreaExtensions.cs │ │ │ ├── ValidationExtensions.cs │ │ │ └── ValueExtensions.cs │ │ ├── Html5DateRenderingMode.cs │ │ ├── HtmlHelper.cs │ │ ├── HtmlHelperOfTModel.cs │ │ ├── HttpDeleteAttribute.cs │ │ ├── HttpFileCollectionValueProvider.cs │ │ ├── HttpFileCollectionValueProviderFactory.cs │ │ ├── HttpGetAttribute.cs │ │ ├── HttpHandlerUtil.cs │ │ ├── HttpHeadAttribute.cs │ │ ├── HttpNotFoundResult.cs │ │ ├── HttpOptionsAttribute.cs │ │ ├── HttpPatchAttribute.cs │ │ ├── HttpPostAttribute.cs │ │ ├── HttpPostedFileBaseModelBinder.cs │ │ ├── HttpPutAttribute.cs │ │ ├── HttpRequestExtensions.cs │ │ ├── HttpStatusCodeResult.cs │ │ ├── HttpUnauthorizedResult.cs │ │ ├── HttpVerbs.cs │ │ ├── IActionFilter.cs │ │ ├── IActionInvoker.cs │ │ ├── IActionInvokerFactory.cs │ │ ├── IAuthorizationFilter.cs │ │ ├── IBuildManager.cs │ │ ├── IClientValidatable.cs │ │ ├── IController.cs │ │ ├── IControllerActivator.cs │ │ ├── IControllerFactory.cs │ │ ├── IDependencyResolver.cs │ │ ├── IEnumerableValueProvider.cs │ │ ├── IExceptionFilter.cs │ │ ├── IFilterProvider.cs │ │ ├── IMetadataAware.cs │ │ ├── IMethodInfoActionDescriptor.cs │ │ ├── IModelBinder.cs │ │ ├── IModelBinderProvider.cs │ │ ├── IMvcControlBuilder.cs │ │ ├── IMvcFilter.cs │ │ ├── IResolver.cs │ │ ├── IResultFilter.cs │ │ ├── IRouteWithArea.cs │ │ ├── ITempDataProvider.cs │ │ ├── ITempDataProviderFactory.cs │ │ ├── IUniquelyIdentifiable.cs │ │ ├── IUnvalidatedRequestValues.cs │ │ ├── IUnvalidatedValueProvider.cs │ │ ├── IValueProvider.cs │ │ ├── IView.cs │ │ ├── IViewDataContainer.cs │ │ ├── IViewEngine.cs │ │ ├── IViewLocationCache.cs │ │ ├── IViewPageActivator.cs │ │ ├── IViewStartPageChild.cs │ │ ├── InputType.cs │ │ ├── JQueryFormValueProvider.cs │ │ ├── JQueryFormValueProviderFactory.cs │ │ ├── JavaScript/ │ │ │ ├── jquery.unobtrusive-ajax.js │ │ │ └── jquery.validate.unobtrusive.js │ │ ├── JavaScriptResult.cs │ │ ├── JsonRequestBehavior.cs │ │ ├── JsonResult.cs │ │ ├── JsonValueProviderFactory.cs │ │ ├── LinqBinaryModelBinder.cs │ │ ├── MaxLengthAttributeAdapter.cs │ │ ├── MembershipPasswordAttributeAdapter.cs │ │ ├── MinLengthAttributeAdapter.cs │ │ ├── ModelBinderAttribute.cs │ │ ├── ModelBinderDictionary.cs │ │ ├── ModelBinderProviderCollection.cs │ │ ├── ModelBinderProviders.cs │ │ ├── ModelBinders.cs │ │ ├── ModelBindingContext.cs │ │ ├── ModelError.cs │ │ ├── ModelErrorCollection.cs │ │ ├── ModelMetadata.cs │ │ ├── ModelMetadataProvider.cs │ │ ├── ModelMetadataProviders.cs │ │ ├── ModelState.cs │ │ ├── ModelStateDictionary.cs │ │ ├── ModelValidationResult.cs │ │ ├── ModelValidator.cs │ │ ├── ModelValidatorProvider.cs │ │ ├── ModelValidatorProviderCollection.cs │ │ ├── ModelValidatorProviders.cs │ │ ├── MultiSelectList.cs │ │ ├── MultiServiceResolver.cs │ │ ├── MvcFilter.cs │ │ ├── MvcHandler.cs │ │ ├── MvcHtmlString.cs │ │ ├── MvcHttpHandler.cs │ │ ├── MvcRouteHandler.cs │ │ ├── MvcWebRazorHostFactory.cs │ │ ├── NameValueCollectionExtensions.cs │ │ ├── NameValueCollectionValueProvider.cs │ │ ├── NoAsyncTimeoutAttribute.cs │ │ ├── NonActionAttribute.cs │ │ ├── NullViewLocationCache.cs │ │ ├── OutputCacheAttribute.cs │ │ ├── OverrideActionFiltersAttribute.cs │ │ ├── OverrideAuthenticationAttribute.cs │ │ ├── OverrideAuthorizationAttribute.cs │ │ ├── OverrideExceptionFiltersAttribute.cs │ │ ├── OverrideResultFiltersAttribute.cs │ │ ├── ParameterBindingInfo.cs │ │ ├── ParameterDescriptor.cs │ │ ├── ParameterInfoUtil.cs │ │ ├── PartialViewResult.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── MvcResources.Designer.cs │ │ │ └── MvcResources.resx │ │ ├── QueryStringValueProvider.cs │ │ ├── QueryStringValueProviderFactory.cs │ │ ├── RangeAttributeAdapter.cs │ │ ├── Razor/ │ │ │ ├── MvcCSharpRazorCodeGenerator.cs │ │ │ ├── MvcCSharpRazorCodeParser.cs │ │ │ ├── MvcVBRazorCodeParser.cs │ │ │ ├── MvcWebPageRazorHost.cs │ │ │ ├── SetModelTypeCodeGenerator.cs │ │ │ └── StartPageLookupDelegate.cs │ │ ├── RazorView.cs │ │ ├── RazorViewEngine.cs │ │ ├── ReaderWriterCache.cs │ │ ├── RedirectResult.cs │ │ ├── RedirectToRouteResult.cs │ │ ├── ReflectedActionDescriptor.cs │ │ ├── ReflectedAttributeCache.cs │ │ ├── ReflectedControllerDescriptor.cs │ │ ├── ReflectedParameterBindingInfo.cs │ │ ├── ReflectedParameterDescriptor.cs │ │ ├── RegularExpressionAttributeAdapter.cs │ │ ├── RemoteAttribute.cs │ │ ├── RequireHttpsAttribute.cs │ │ ├── RequiredAttributeAdapter.cs │ │ ├── ResultExecutedContext.cs │ │ ├── ResultExecutingContext.cs │ │ ├── RouteAreaAttribute.cs │ │ ├── RouteAttribute.cs │ │ ├── RouteCollectionExtensions.cs │ │ ├── RouteDataValueProvider.cs │ │ ├── RouteDataValueProviderFactory.cs │ │ ├── RoutePrefixAttribute.cs │ │ ├── RouteValuesHelpers.cs │ │ ├── Routing/ │ │ │ ├── AttributeRoutingMapper.cs │ │ │ ├── ConstraintValidation.cs │ │ │ ├── DefaultDirectRouteProvider.cs │ │ │ ├── DirectRouteCandidate.cs │ │ │ ├── DirectRouteExtensions.cs │ │ │ ├── IDirectRouteProvider.cs │ │ │ ├── IRouteInfoProvider.cs │ │ │ ├── LinkGenerationRoute.cs │ │ │ ├── ParsedRoute.cs │ │ │ ├── RouteBuilder.cs │ │ │ ├── RouteCollectionAttributeRoutingExtensions.cs │ │ │ ├── RouteCollectionRoute.cs │ │ │ └── RouteDataTokenKeys.cs │ │ ├── SelectList.cs │ │ ├── SelectListGroup.cs │ │ ├── SelectListItem.cs │ │ ├── SessionStateAttribute.cs │ │ ├── SessionStateTempDataProvider.cs │ │ ├── SingleServiceResolver.cs │ │ ├── StringLengthAttributeAdapter.cs │ │ ├── System.Web.Mvc.csproj │ │ ├── TagBuilderExtensions.cs │ │ ├── TempDataDictionary.cs │ │ ├── TemplateInfo.cs │ │ ├── TryGetValueDelegate.cs │ │ ├── TypeCacheSerializer.cs │ │ ├── TypeCacheUtil.cs │ │ ├── TypeDescriptorHelper.cs │ │ ├── TypeHelpers.cs │ │ ├── UnvalidatedRequestValuesAccessor.cs │ │ ├── UnvalidatedRequestValuesWrapper.cs │ │ ├── UrlHelper.cs │ │ ├── UrlParameter.cs │ │ ├── ValidatableObjectAdapter.cs │ │ ├── ValidateAntiForgeryTokenAttribute.cs │ │ ├── ValidateInputAttribute.cs │ │ ├── ValueProviderCollection.cs │ │ ├── ValueProviderDictionary.cs │ │ ├── ValueProviderFactories.cs │ │ ├── ValueProviderFactory.cs │ │ ├── ValueProviderFactoryCollection.cs │ │ ├── ValueProviderResult.cs │ │ ├── ValueProviderUtil.cs │ │ ├── ViewContext.cs │ │ ├── ViewDataDictionary.cs │ │ ├── ViewDataDictionaryOfTModel.cs │ │ ├── ViewDataInfo.cs │ │ ├── ViewEngineCollection.cs │ │ ├── ViewEngineResult.cs │ │ ├── ViewEngines.cs │ │ ├── ViewMasterPage.cs │ │ ├── ViewMasterPageControlBuilder.cs │ │ ├── ViewMasterPageOfTModel.cs │ │ ├── ViewPage.cs │ │ ├── ViewPageControlBuilder.cs │ │ ├── ViewPageOfTModel.cs │ │ ├── ViewResult.cs │ │ ├── ViewResultBase.cs │ │ ├── ViewStartPage.cs │ │ ├── ViewTemplateUserControl.cs │ │ ├── ViewTemplateUserControlOfTModel.cs │ │ ├── ViewType.cs │ │ ├── ViewTypeControlBuilder.cs │ │ ├── ViewTypeParserFilter.cs │ │ ├── ViewUserControl.cs │ │ ├── ViewUserControlControlBuilder.cs │ │ ├── ViewUserControlOfTModel.cs │ │ ├── VirtualPathProviderViewEngine.cs │ │ ├── WebFormView.cs │ │ ├── WebFormViewEngine.cs │ │ ├── WebViewPage.cs │ │ ├── WebViewPageOfTModel.cs │ │ └── packages.config │ ├── System.Web.Razor/ │ │ ├── CSharpRazorCodeLanguage.cs │ │ ├── DocumentParseCompleteEventArgs.cs │ │ ├── Editor/ │ │ │ ├── AutoCompleteEditHandler.cs │ │ │ ├── BackgroundParser.cs │ │ │ ├── EditResult.cs │ │ │ ├── EditorHints.cs │ │ │ ├── ImplicitExpressionEditHandler.cs │ │ │ ├── RazorEditorTrace.cs │ │ │ ├── SingleLineMarkupEditHandler.cs │ │ │ └── SpanEditHandler.cs │ │ ├── Generator/ │ │ │ ├── AddImportCodeGenerator.cs │ │ │ ├── AttributeBlockCodeGenerator.cs │ │ │ ├── BaseCodeWriter.cs │ │ │ ├── BlockCodeGenerator.cs │ │ │ ├── CSharpCodeWriter.cs │ │ │ ├── CSharpRazorCodeGenerator.cs │ │ │ ├── CodeGenerationCompleteEventArgs.cs │ │ │ ├── CodeGeneratorContext.cs │ │ │ ├── CodeGeneratorPaddingHelper.cs │ │ │ ├── CodeWriter.cs │ │ │ ├── CodeWriterExtensions.cs │ │ │ ├── DynamicAttributeBlockCodeGenerator.cs │ │ │ ├── ExpressionCodeGenerator.cs │ │ │ ├── ExpressionRenderingMode.cs │ │ │ ├── GeneratedClassContext.cs │ │ │ ├── GeneratedCodeMapping.cs │ │ │ ├── HelperCodeGenerator.cs │ │ │ ├── HybridCodeGenerator.cs │ │ │ ├── IBlockCodeGenerator.cs │ │ │ ├── ISpanCodeGenerator.cs │ │ │ ├── LiteralAttributeCodeGenerator.cs │ │ │ ├── MarkupCodeGenerator.cs │ │ │ ├── RazorCodeGenerator.cs │ │ │ ├── RazorCommentCodeGenerator.cs │ │ │ ├── RazorDirectiveAttributeCodeGenerator.cs │ │ │ ├── ResolveUrlCodeGenerator.cs │ │ │ ├── SectionCodeGenerator.cs │ │ │ ├── SetBaseTypeCodeGenerator.cs │ │ │ ├── SetLayoutCodeGenerator.cs │ │ │ ├── SetVBOptionCodeGenerator.cs │ │ │ ├── SpanCodeGenerator.cs │ │ │ ├── StatementCodeGenerator.cs │ │ │ ├── TemplateBlockCodeGenerator.cs │ │ │ ├── TypeMemberCodeGenerator.cs │ │ │ ├── VBCodeWriter.cs │ │ │ └── VBRazorCodeGenerator.cs │ │ ├── GeneratorResults.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Parser/ │ │ │ ├── BalancingModes.cs │ │ │ ├── CSharpCodeParser.Directives.cs │ │ │ ├── CSharpCodeParser.Statements.cs │ │ │ ├── CSharpCodeParser.cs │ │ │ ├── CSharpLanguageCharacteristics.cs │ │ │ ├── CallbackVisitor.cs │ │ │ ├── ConditionalAttributeCollapser.cs │ │ │ ├── HtmlLanguageCharacteristics.cs │ │ │ ├── HtmlMarkupParser.Block.cs │ │ │ ├── HtmlMarkupParser.Document.cs │ │ │ ├── HtmlMarkupParser.Section.cs │ │ │ ├── HtmlMarkupParser.cs │ │ │ ├── ISyntaxTreeRewriter.cs │ │ │ ├── LanguageCharacteristics.cs │ │ │ ├── MarkupCollapser.cs │ │ │ ├── MarkupRewriter.cs │ │ │ ├── ParserBase.cs │ │ │ ├── ParserContext.cs │ │ │ ├── ParserHelpers.cs │ │ │ ├── ParserVisitor.cs │ │ │ ├── ParserVisitorExtensions.cs │ │ │ ├── RazorParser.cs │ │ │ ├── SyntaxConstants.cs │ │ │ ├── SyntaxTree/ │ │ │ │ ├── AcceptedCharacters.cs │ │ │ │ ├── Block.cs │ │ │ │ ├── BlockBuilder.cs │ │ │ │ ├── BlockType.cs │ │ │ │ ├── EquivalenceComparer.cs │ │ │ │ ├── RazorError.cs │ │ │ │ ├── Span.cs │ │ │ │ ├── SpanBuilder.cs │ │ │ │ ├── SpanKind.cs │ │ │ │ └── SyntaxTreeNode.cs │ │ │ ├── TextReaderExtensions.cs │ │ │ ├── TokenizerBackedParser.Helpers.cs │ │ │ ├── TokenizerBackedParser.cs │ │ │ ├── VBCodeParser.Directives.cs │ │ │ ├── VBCodeParser.Statements.cs │ │ │ ├── VBCodeParser.cs │ │ │ ├── VBLanguageCharacteristics.cs │ │ │ └── WhitespaceRewriter.cs │ │ ├── ParserResults.cs │ │ ├── PartialParseResult.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RazorCodeLanguage.cs │ │ ├── RazorDebugHelpers.cs │ │ ├── RazorDirectiveAttribute.cs │ │ ├── RazorEditorParser.cs │ │ ├── RazorEngineHost.cs │ │ ├── RazorTemplateEngine.cs │ │ ├── Resources/ │ │ │ ├── RazorResources.Designer.cs │ │ │ └── RazorResources.resx │ │ ├── StateMachine.cs │ │ ├── System.Web.Razor.csproj │ │ ├── Text/ │ │ │ ├── BufferingTextReader.cs │ │ │ ├── ITextBuffer.cs │ │ │ ├── LineTrackingStringBuffer.cs │ │ │ ├── LocationTagged.cs │ │ │ ├── LookaheadTextReader.cs │ │ │ ├── LookaheadToken.cs │ │ │ ├── SeekableTextReader.cs │ │ │ ├── SourceLocation.cs │ │ │ ├── SourceLocationTracker.cs │ │ │ ├── TextBufferReader.cs │ │ │ ├── TextChange.cs │ │ │ ├── TextChangeType.cs │ │ │ ├── TextDocumentReader.cs │ │ │ └── TextExtensions.cs │ │ ├── Tokenizer/ │ │ │ ├── CSharpHelpers.cs │ │ │ ├── CSharpKeywordDetector.cs │ │ │ ├── CSharpTokenizer.cs │ │ │ ├── HtmlTokenizer.cs │ │ │ ├── ITokenizer.cs │ │ │ ├── Symbols/ │ │ │ │ ├── CSharpKeyword.cs │ │ │ │ ├── CSharpSymbol.cs │ │ │ │ ├── CSharpSymbolType.cs │ │ │ │ ├── HtmlSymbol.cs │ │ │ │ ├── HtmlSymbolType.cs │ │ │ │ ├── ISymbol.cs │ │ │ │ ├── KnownSymbolType.cs │ │ │ │ ├── SymbolBase.cs │ │ │ │ ├── SymbolExtensions.cs │ │ │ │ ├── SymbolTypeSuppressions.cs │ │ │ │ ├── VBKeyword.cs │ │ │ │ ├── VBSymbol.cs │ │ │ │ └── VBSymbolType.cs │ │ │ ├── Tokenizer.cs │ │ │ ├── TokenizerView.cs │ │ │ ├── VBHelpers.cs │ │ │ ├── VBKeywordDetector.cs │ │ │ ├── VBTokenizer.cs │ │ │ └── XmlHelpers.cs │ │ ├── Utils/ │ │ │ ├── CharUtils.cs │ │ │ ├── DisposableAction.cs │ │ │ ├── EnumUtil.cs │ │ │ └── EnumeratorExtensions.cs │ │ └── VBRazorCodeLanguage.cs │ ├── System.Web.WebPages/ │ │ ├── ApplicationPart.cs │ │ ├── ApplicationParts/ │ │ │ ├── ApplicationPartRegistry.cs │ │ │ ├── DictionaryBasedVirtualPathFactory.cs │ │ │ ├── IResourceAssembly.cs │ │ │ ├── LazyAction.cs │ │ │ ├── ResourceAssembly.cs │ │ │ ├── ResourceHandler.cs │ │ │ └── ResourceRouteHandler.cs │ │ ├── ApplicationStartPage.cs │ │ ├── AttributeValue.cs │ │ ├── BrowserHelpers.cs │ │ ├── BrowserOverride.cs │ │ ├── BrowserOverrideStore.cs │ │ ├── BrowserOverrideStores.cs │ │ ├── BuildManagerWrapper.cs │ │ ├── Common/ │ │ │ └── DisposableAction.cs │ │ ├── CookieBrowserOverrideStore.cs │ │ ├── DefaultDisplayMode.cs │ │ ├── DisplayInfo.cs │ │ ├── DisplayModeProvider.cs │ │ ├── DynamicHttpApplicationState.cs │ │ ├── DynamicPageDataDictionary.cs │ │ ├── FileExistenceCache.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HelperPage.cs │ │ ├── HelperResult.cs │ │ ├── Helpers/ │ │ │ ├── AntiForgery.cs │ │ │ ├── AntiForgeryConfig.cs │ │ │ ├── AntiXsrf/ │ │ │ │ ├── AntiForgeryConfigWrapper.cs │ │ │ │ ├── AntiForgeryToken.cs │ │ │ │ ├── AntiForgeryTokenSerializer.cs │ │ │ │ ├── AntiForgeryTokenStore.cs │ │ │ │ ├── AntiForgeryWorker.cs │ │ │ │ ├── BinaryBlob.cs │ │ │ │ ├── ClaimUidExtractor.cs │ │ │ │ ├── IAntiForgeryConfig.cs │ │ │ │ ├── IAntiForgeryTokenSerializer.cs │ │ │ │ ├── IClaimUidExtractor.cs │ │ │ │ ├── ICryptoSystem.cs │ │ │ │ ├── ITokenStore.cs │ │ │ │ ├── ITokenValidator.cs │ │ │ │ ├── MachineKey45CryptoSystem.cs │ │ │ │ └── TokenValidator.cs │ │ │ ├── Claims/ │ │ │ │ ├── Claim.cs │ │ │ │ ├── ClaimsIdentity.cs │ │ │ │ └── ClaimsIdentityConverter.cs │ │ │ ├── CryptoUtil.cs │ │ │ ├── IAntiForgeryAdditionalDataProvider.cs │ │ │ ├── UnvalidatedRequestValues.cs │ │ │ └── Validation.cs │ │ ├── Html/ │ │ │ ├── HtmlHelper.Checkbox.cs │ │ │ ├── HtmlHelper.Input.cs │ │ │ ├── HtmlHelper.Internal.cs │ │ │ ├── HtmlHelper.Label.cs │ │ │ ├── HtmlHelper.Radio.cs │ │ │ ├── HtmlHelper.Select.cs │ │ │ ├── HtmlHelper.TextArea.cs │ │ │ ├── HtmlHelper.Validation.cs │ │ │ ├── HtmlHelper.cs │ │ │ ├── ModelState.cs │ │ │ ├── ModelStateDictionary.cs │ │ │ └── SelectListItem.cs │ │ ├── HttpContextExtensions.cs │ │ ├── IDisplayMode.cs │ │ ├── ITemplateFile.cs │ │ ├── IVirtualPathFactory.cs │ │ ├── IVirtualPathUtility.cs │ │ ├── IWebPageRequestExecutor.cs │ │ ├── Instrumentation/ │ │ │ ├── HttpContextAdapter.Availability.cs │ │ │ ├── HttpContextAdapter.generated.cs │ │ │ ├── HttpContextAdapter.tt │ │ │ ├── InstrumentationService.cs │ │ │ ├── PageExecutionContextAdapter.generated.cs │ │ │ ├── PageExecutionContextAdapter.tt │ │ │ ├── PageExecutionListenerAdapter.generated.cs │ │ │ ├── PageExecutionListenerAdapter.tt │ │ │ ├── PageInstrumentationServiceAdapter.cs │ │ │ └── PositionTagged.cs │ │ ├── MimeMapping.cs │ │ ├── Mvc/ │ │ │ ├── HttpAntiForgeryException.cs │ │ │ ├── ModelClientValidationEqualToRule.cs │ │ │ ├── ModelClientValidationMaxLengthRule.cs │ │ │ ├── ModelClientValidationMembershipPasswordRule.cs │ │ │ ├── ModelClientValidationMinLengthRule.cs │ │ │ ├── ModelClientValidationRangeRule.cs │ │ │ ├── ModelClientValidationRegexRule.cs │ │ │ ├── ModelClientValidationRemoteRule.cs │ │ │ ├── ModelClientValidationRequiredRule.cs │ │ │ ├── ModelClientValidationRule.cs │ │ │ ├── ModelClientValidationStringLengthRule.cs │ │ │ ├── TagBuilder.cs │ │ │ ├── TagRenderMode.cs │ │ │ └── UnobtrusiveValidationAttributesGenerator.cs │ │ ├── PageDataDictionary.cs │ │ ├── PageVirtualPathAttribute.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── ReflectionDynamicObject.cs │ │ ├── RequestBrowserOverrideStore.cs │ │ ├── RequestExtensions.cs │ │ ├── RequestResourceTracker.cs │ │ ├── Resources/ │ │ │ ├── WebPageResources.Designer.cs │ │ │ └── WebPageResources.resx │ │ ├── ResponseExtensions.cs │ │ ├── Scope/ │ │ │ ├── ApplicationScopeStorageDictionary.cs │ │ │ ├── AspNetRequestScopeStorageProvider.cs │ │ │ ├── IScopeStorageProvider.cs │ │ │ ├── ScopeStorage.cs │ │ │ ├── ScopeStorageComparer.cs │ │ │ ├── ScopeStorageDictionary.cs │ │ │ ├── StaticScopeStorageProvider.cs │ │ │ └── WebConfigScopeStorageDictionary.cs │ │ ├── SectionWriter.cs │ │ ├── StartPage.cs │ │ ├── StringExtensions.cs │ │ ├── StringWriterExtensions.cs │ │ ├── System.Web.WebPages.csproj │ │ ├── TemplateFileInfo.cs │ │ ├── TemplateStack.cs │ │ ├── UrlDataList.cs │ │ ├── Utils/ │ │ │ ├── BuildManagerExceptionUtil.cs │ │ │ ├── CultureUtil.cs │ │ │ ├── HtmlAttributePropertyHelper.cs │ │ │ ├── PathUtil.cs │ │ │ ├── SessionStateUtil.cs │ │ │ ├── TypeHelper.cs │ │ │ ├── UrlRewriterHelper.cs │ │ │ └── UrlUtil.cs │ │ ├── Validation/ │ │ │ ├── CompareValidator.cs │ │ │ ├── DataTypeValidator.cs │ │ │ ├── IValidator.cs │ │ │ ├── RequestFieldValidatorBase.cs │ │ │ ├── ValidationAttributeAdapter.cs │ │ │ ├── ValidationHelper.cs │ │ │ └── Validator.cs │ │ ├── VirtualPathFactoryExtensions.cs │ │ ├── VirtualPathFactoryManager.cs │ │ ├── VirtualPathUtilityWrapper.cs │ │ ├── WebPage.cs │ │ ├── WebPageBase.cs │ │ ├── WebPageContext.cs │ │ ├── WebPageExecutingBase.cs │ │ ├── WebPageHttpHandler.cs │ │ ├── WebPageHttpModule.cs │ │ ├── WebPageMatch.cs │ │ ├── WebPageRenderingBase.cs │ │ ├── WebPageRoute.cs │ │ └── packages.config │ ├── System.Web.WebPages.Administration/ │ │ ├── Default.cshtml │ │ ├── EnableInstructions.cshtml │ │ ├── Framework/ │ │ │ ├── AdminSecurity.cs │ │ │ ├── PreApplicationStartCode.cs │ │ │ └── SiteAdmin.cs │ │ ├── Login.cshtml │ │ ├── Logout.cshtml │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Register.cshtml │ │ ├── Resources/ │ │ │ ├── AdminResources.Designer.cs │ │ │ ├── AdminResources.resx │ │ │ ├── PackageManagerResources.Designer.cs │ │ │ └── PackageManagerResources.resx │ │ ├── Site.css │ │ ├── System.Web.WebPages.Administration.csproj │ │ ├── _Layout.cshtml │ │ ├── _pagestart.cshtml │ │ └── packages.config │ ├── System.Web.WebPages.Deployment/ │ │ ├── AppDomainHelper.cs │ │ ├── AssemblyUtils.cs │ │ ├── BuildManagerWrapper.cs │ │ ├── Common/ │ │ │ ├── IFileSystem.cs │ │ │ └── PhysicalFileSystem.cs │ │ ├── GlobalSuppressions.cs │ │ ├── IBuildManager.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ ├── ConfigurationResources.Designer.cs │ │ │ └── ConfigurationResources.resx │ │ ├── System.Web.WebPages.Deployment.csproj │ │ ├── WebPagesDeployment.cs │ │ └── packages.config │ ├── System.Web.WebPages.Razor/ │ │ ├── AssemblyBuilderWrapper.cs │ │ ├── CompilingPathEventArgs.cs │ │ ├── Configuration/ │ │ │ ├── HostSection.cs │ │ │ ├── RazorPagesSection.cs │ │ │ └── RazorWebSectionGroup.cs │ │ ├── GlobalSuppressions.cs │ │ ├── HostingEnvironmentWrapper.cs │ │ ├── IAssemblyBuilder.cs │ │ ├── IHostingEnvironment.cs │ │ ├── PreApplicationStartCode.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RazorBuildProvider.cs │ │ ├── Resources/ │ │ │ ├── RazorWebResources.Designer.cs │ │ │ └── RazorWebResources.resx │ │ ├── System.Web.WebPages.Razor.csproj │ │ ├── WebCodeRazorHost.cs │ │ ├── WebPageRazorHost.cs │ │ └── WebRazorHostFactory.cs │ ├── WebApiHelpPage/ │ │ ├── Areas/ │ │ │ └── HelpPage/ │ │ │ ├── ApiDescriptionExtensions.cs │ │ │ ├── App_Start/ │ │ │ │ └── HelpPageConfig.cs │ │ │ ├── Controllers/ │ │ │ │ └── HelpController.cs │ │ │ ├── HelpPage.css │ │ │ ├── HelpPageAreaRegistration.cs │ │ │ ├── HelpPageConfigurationExtensions.cs │ │ │ ├── ModelDescriptions/ │ │ │ │ ├── CollectionModelDescription.cs │ │ │ │ ├── ComplexTypeModelDescription.cs │ │ │ │ ├── DictionaryModelDescription.cs │ │ │ │ ├── EnumTypeModelDescription.cs │ │ │ │ ├── EnumValueDescription.cs │ │ │ │ ├── IModelDocumentationProvider.cs │ │ │ │ ├── KeyValuePairModelDescription.cs │ │ │ │ ├── ModelDescription.cs │ │ │ │ ├── ModelDescriptionGenerator.cs │ │ │ │ ├── ModelNameAttribute.cs │ │ │ │ ├── ModelNameHelper.cs │ │ │ │ ├── ParameterAnnotation.cs │ │ │ │ ├── ParameterDescription.cs │ │ │ │ └── SimpleTypeModelDescription.cs │ │ │ ├── Models/ │ │ │ │ └── HelpPageApiModel.cs │ │ │ ├── SampleGeneration/ │ │ │ │ ├── HelpPageSampleGenerator.cs │ │ │ │ ├── HelpPageSampleKey.cs │ │ │ │ ├── ImageSample.cs │ │ │ │ ├── InvalidSample.cs │ │ │ │ ├── ObjectGenerator.cs │ │ │ │ ├── SampleDirection.cs │ │ │ │ └── TextSample.cs │ │ │ ├── Views/ │ │ │ │ ├── Help/ │ │ │ │ │ ├── Api.cshtml │ │ │ │ │ ├── DisplayTemplates/ │ │ │ │ │ │ ├── ApiGroup.cshtml │ │ │ │ │ │ ├── CollectionModelDescription.cshtml │ │ │ │ │ │ ├── ComplexTypeModelDescription.cshtml │ │ │ │ │ │ ├── DictionaryModelDescription.cshtml │ │ │ │ │ │ ├── EnumTypeModelDescription.cshtml │ │ │ │ │ │ ├── HelpPageApiModel.cshtml │ │ │ │ │ │ ├── ImageSample.cshtml │ │ │ │ │ │ ├── InvalidSample.cshtml │ │ │ │ │ │ ├── KeyValuePairModelDescription.cshtml │ │ │ │ │ │ ├── ModelDescriptionLink.cshtml │ │ │ │ │ │ ├── Parameters.cshtml │ │ │ │ │ │ ├── Samples.cshtml │ │ │ │ │ │ ├── SimpleTypeModelDescription.cshtml │ │ │ │ │ │ └── TextSample.cshtml │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── ResourceModel.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ ├── Web.config │ │ │ │ └── _ViewStart.cshtml │ │ │ └── XmlDocumentationProvider.cs │ │ ├── GlobalSuppressions.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Settings.StyleCop │ │ ├── VB/ │ │ │ ├── Areas/ │ │ │ │ └── HelpPage/ │ │ │ │ ├── ApiDescriptionExtensions.vb │ │ │ │ ├── App_Start/ │ │ │ │ │ └── HelpPageConfig.vb │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HelpController.vb │ │ │ │ ├── HelpPage.css │ │ │ │ ├── HelpPageAreaRegistration.vb │ │ │ │ ├── HelpPageConfigurationExtensions.vb │ │ │ │ ├── ModelDescriptions/ │ │ │ │ │ ├── CollectionModelDescription.vb │ │ │ │ │ ├── ComplexTypeModelDescription.vb │ │ │ │ │ ├── DictionaryModelDescription.vb │ │ │ │ │ ├── EnumTypeModelDescription.vb │ │ │ │ │ ├── EnumValueDescription.vb │ │ │ │ │ ├── IModelDocumentationProvider.vb │ │ │ │ │ ├── KeyValuePairModelDescription.vb │ │ │ │ │ ├── ModelDescription.vb │ │ │ │ │ ├── ModelDescriptionGenerator.vb │ │ │ │ │ ├── ModelNameAttribute.vb │ │ │ │ │ ├── ModelNameHelper.vb │ │ │ │ │ ├── ParameterAnnotation.vb │ │ │ │ │ ├── ParameterDescription.vb │ │ │ │ │ └── SimpleTypeModelDescription.vb │ │ │ │ ├── Models/ │ │ │ │ │ └── HelpPageApiModel.vb │ │ │ │ ├── SampleGeneration/ │ │ │ │ │ ├── HelpPageSampleGenerator.vb │ │ │ │ │ ├── HelpPageSampleKey.vb │ │ │ │ │ ├── ImageSample.vb │ │ │ │ │ ├── InvalidSample.vb │ │ │ │ │ ├── ObjectGenerator.vb │ │ │ │ │ ├── SampleDirection.vb │ │ │ │ │ └── TextSample.vb │ │ │ │ ├── Views/ │ │ │ │ │ ├── Help/ │ │ │ │ │ │ ├── Api.vbhtml │ │ │ │ │ │ ├── DisplayTemplates/ │ │ │ │ │ │ │ ├── ApiGroup.vbhtml │ │ │ │ │ │ │ ├── CollectionModelDescription.vbhtml │ │ │ │ │ │ │ ├── ComplexTypeModelDescription.vbhtml │ │ │ │ │ │ │ ├── DictionaryModelDescription.vbhtml │ │ │ │ │ │ │ ├── EnumTypeModelDescription.vbhtml │ │ │ │ │ │ │ ├── HelpPageApiModel.vbhtml │ │ │ │ │ │ │ ├── ImageSample.vbhtml │ │ │ │ │ │ │ ├── InvalidSample.vbhtml │ │ │ │ │ │ │ ├── KeyValuePairModelDescription.vbhtml │ │ │ │ │ │ │ ├── ModelDescriptionLink.vbhtml │ │ │ │ │ │ │ ├── Parameters.vbhtml │ │ │ │ │ │ │ ├── Samples.vbhtml │ │ │ │ │ │ │ ├── SimpleTypeModelDescription.vbhtml │ │ │ │ │ │ │ └── TextSample.vbhtml │ │ │ │ │ │ ├── Index.vbhtml │ │ │ │ │ │ └── ResourceModel.vbhtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ └── _Layout.vbhtml │ │ │ │ │ ├── Web.config │ │ │ │ │ └── _ViewStart.vbhtml │ │ │ │ └── XmlDocumentationProvider.vb │ │ │ ├── GlobalSuppressions.vb │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.vb │ │ │ ├── Settings.StyleCop │ │ │ ├── WebApiHelpPage.VB.nuspec │ │ │ ├── WebApiHelpPageVB.vbproj │ │ │ └── packages.config │ │ ├── WebApiHelpPage.csproj │ │ ├── WebApiHelpPage.nuspec │ │ ├── WebApiHelpPageMsBuildTasks.targets │ │ └── packages.config │ ├── WebHelpers.ruleset │ ├── WebMatrix.Data/ │ │ ├── ConfigurationManagerWrapper.cs │ │ ├── ConnectionConfiguration.cs │ │ ├── ConnectionEventArgs.cs │ │ ├── Database.cs │ │ ├── DbProviderFactoryWrapper.cs │ │ ├── DynamicRecord.cs │ │ ├── GlobalSuppressions.cs │ │ ├── IConfigurationManager.cs │ │ ├── IConnectionConfiguration.cs │ │ ├── IDbFileHandler.cs │ │ ├── IDbProviderFactory.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ ├── DataResources.Designer.cs │ │ │ └── DataResources.resx │ │ ├── SqlCeDbFileHandler.cs │ │ ├── SqlServerDbFileHandler.cs │ │ └── WebMatrix.Data.csproj │ └── WebMatrix.WebData/ │ ├── ConfigUtil.cs │ ├── DatabaseConnectionInfo.cs │ ├── DatabaseWrapper.cs │ ├── ExtendedMembershipProvider.cs │ ├── FormsAuthenticationSettings.cs │ ├── GlobalSuppressions.cs │ ├── IDatabase.cs │ ├── OAuthAccountData.cs │ ├── PreApplicationStartCode.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Resources/ │ │ ├── WebDataResources.Designer.cs │ │ └── WebDataResources.resx │ ├── SimpleMembershipProvider.cs │ ├── SimpleMembershipProviderCasingBehavior.cs │ ├── SimpleRoleProvider.cs │ ├── WebMatrix.WebData.csproj │ └── WebSecurity.cs ├── test/ │ ├── Common/ │ │ ├── AttributeListTest.cs │ │ ├── CollectionExtensionsTest.cs │ │ ├── DictionaryExtensionsTest.cs │ │ ├── ErrorTest.cs │ │ ├── HttpMethodHelperTest.cs │ │ ├── ListWrapperCollectionTests.cs │ │ ├── PathHelpersTest.cs │ │ ├── PrefixContainerTest.cs │ │ ├── Routing/ │ │ │ ├── DefaultInlineConstraintResolverTest.cs │ │ │ ├── DirectRouteBuilderTests.cs │ │ │ ├── InlineRouteTemplateParserTests.cs │ │ │ ├── RouteConstraintsTests.cs │ │ │ ├── RouteFactoryAttributeTests.cs │ │ │ ├── RoutePrecedenceTests.cs │ │ │ └── SubRouteCollectionTest.cs │ │ ├── TaskHelpersExtensionsTest.cs │ │ ├── TaskHelpersTest.cs │ │ ├── TypeExtensionsTest.cs │ │ └── UriQueryUtilityTest.cs │ ├── Directory.Build.props │ ├── Directory.Build.targets │ ├── Microsoft.AspNet.Facebook.Test/ │ │ ├── App.config │ │ ├── DefaultFacebookClientProviderTest.cs │ │ ├── DefaultFacebookPermissionServiceTest.cs │ │ ├── FacebookAuthorizeAttributeTest.cs │ │ ├── FacebookAuthorizeFilterHookTest.cs │ │ ├── FacebookAuthorizeFilterTest.cs │ │ ├── FacebookClientExtensionsTest.cs │ │ ├── FacebookConfigurationTest.cs │ │ ├── FacebookContextModelBinderTest.cs │ │ ├── FacebookQueryHelperTest.cs │ │ ├── FacebookRealtimeControllerTest.cs │ │ ├── FacebookRedirectContextModelBinderTest.cs │ │ ├── GlobalFacebookConfigurationTest.cs │ │ ├── Helpers/ │ │ │ ├── LocalFacebookClient.cs │ │ │ └── MockHelpers.cs │ │ ├── Microsoft.AspNet.Facebook.Test.csproj │ │ ├── Types/ │ │ │ ├── FacebookPicture.cs │ │ │ ├── SimpleUser.cs │ │ │ ├── UserPhoto.cs │ │ │ ├── UserStatus.cs │ │ │ ├── UserTypeWithFieldModifiers.cs │ │ │ ├── UserTypeWithIgnoredProperties.cs │ │ │ ├── UserTypeWithRenamedProperties.cs │ │ │ ├── UserTypesWithCycles.cs │ │ │ └── UserWithFriends.cs │ │ └── packages.config │ ├── Microsoft.TestCommon/ │ │ ├── AppDomainUtils.cs │ │ ├── Assert.cs │ │ ├── CultureReplacer.cs │ │ ├── DataAttribute.cs │ │ ├── DictionaryEqualityComparer.cs │ │ ├── Directory.Build.props │ │ ├── EnumHelperTestBase.cs │ │ ├── ExceptionAssertions.cs │ │ ├── ExceptionUtility.cs │ │ ├── FactAttribute.cs │ │ ├── FactDiscoverer.cs │ │ ├── ForceGCAttribute.cs │ │ ├── InlineDataAttribute.cs │ │ ├── MatrixTheoryDataSet.cs │ │ ├── MemberHelper.cs │ │ ├── Microsoft/ │ │ │ └── TestCommon/ │ │ │ ├── DataSets/ │ │ │ │ ├── CommonUnitTestDataSets.cs │ │ │ │ ├── RefTypeTestData.cs │ │ │ │ ├── TestData.cs │ │ │ │ ├── TestDataHolder.cs │ │ │ │ ├── TestDataVariations.cs │ │ │ │ └── ValueTypeTestData.cs │ │ │ ├── GenericTypeAssert.cs │ │ │ ├── HttpAssert.cs │ │ │ ├── MediaTypeAssert.cs │ │ │ ├── MediaTypeHeaderValueComparer.cs │ │ │ ├── ParsedMediaTypeHeaderValue.cs │ │ │ ├── RegexReplacement.cs │ │ │ ├── RuntimeEnvironment.cs │ │ │ ├── SerializerAssert.cs │ │ │ ├── StreamAssert.cs │ │ │ ├── TaskAssert.cs │ │ │ ├── TestDataSetAttribute.cs │ │ │ ├── TimeoutConstant.cs │ │ │ ├── TypeAssert.cs │ │ │ ├── Types/ │ │ │ │ ├── ByteEnum.cs │ │ │ │ ├── FlagsEnum.cs │ │ │ │ ├── INameAndIdContainer.cs │ │ │ │ ├── ISerializableType.cs │ │ │ │ ├── LongEnum.cs │ │ │ │ ├── SByteEnum.cs │ │ │ │ ├── ShortEnum.cs │ │ │ │ ├── SimpleEnum.cs │ │ │ │ ├── UIntEnum.cs │ │ │ │ └── UShortEnum.cs │ │ │ └── XmlAssert.cs │ │ ├── Microsoft.TestCommon.csproj │ │ ├── Platform.cs │ │ ├── PlatformInfo.cs │ │ ├── PortReserver.cs │ │ ├── PreAppStartTestHelper.cs │ │ ├── PreserveSyncContextAttribute.cs │ │ ├── PropertyDataAttribute.cs │ │ ├── ReflectionAssert.cs │ │ ├── ReplaceCultureAttribute.cs │ │ ├── RestoreThreadPrincipalAttribute.cs │ │ ├── SkippedXunitTestCase.cs │ │ ├── TestFile.cs │ │ ├── TestHelper.cs │ │ ├── TheoryAttribute.cs │ │ ├── TheoryDataSet.cs │ │ ├── TheoryDiscoverer.cs │ │ ├── ThreadPoolSyncContext.cs │ │ ├── TraitAttribute.cs │ │ ├── VersionTestHelper.cs │ │ ├── WebUtils.cs │ │ └── xunit.runner.json │ ├── Microsoft.Web.Helpers.Test/ │ │ ├── AnalyticsTest.cs │ │ ├── FacebookTest.cs │ │ ├── FileUploadTest.cs │ │ ├── GamerCardTest.cs │ │ ├── GravatarTest.cs │ │ ├── LinkShareTest.cs │ │ ├── MapsTest.cs │ │ ├── Microsoft.Web.Helpers.Test.csproj │ │ ├── PreAppStartCodeTest.cs │ │ ├── ReCaptchaTest.cs │ │ ├── ThemesTest.cs │ │ ├── UrlBuilderTest.cs │ │ ├── VideoTest.cs │ │ └── packages.config │ ├── Microsoft.Web.Mvc.Test/ │ │ ├── Controls/ │ │ │ └── Test/ │ │ │ ├── DesignModeSite.cs │ │ │ ├── DropDownListTest.cs │ │ │ ├── MvcControlTest.cs │ │ │ ├── MvcTestHelper.cs │ │ │ └── ViewDataContainer.cs │ │ ├── Microsoft.Web.Mvc.Test.csproj │ │ ├── ModelBinding/ │ │ │ └── Test/ │ │ │ ├── ArrayModelBinderProviderTest.cs │ │ │ ├── ArrayModelBinderTest.cs │ │ │ ├── BinaryDataModelBinderProviderTest.cs │ │ │ ├── BindingBehaviorAttributeTest.cs │ │ │ ├── CollectionModelBinderProviderTest.cs │ │ │ ├── CollectionModelBinderTest.cs │ │ │ ├── CollectionModelBinderUtilTest.cs │ │ │ ├── ComplexModelDtoModelBinderProviderTest.cs │ │ │ ├── ComplexModelDtoModelBinderTest.cs │ │ │ ├── ComplexModelDtoResultTest.cs │ │ │ ├── ComplexModelDtoTest.cs │ │ │ ├── DictionaryModelBinderProviderTest.cs │ │ │ ├── DictionaryModelBinderTest.cs │ │ │ ├── ExtensibleModelBinderAdapterTest.cs │ │ │ ├── ExtensibleModelBindingContextTest.cs │ │ │ ├── GenericModelBinderProviderTest.cs │ │ │ ├── KeyValuePairModelBinderProviderTest.cs │ │ │ ├── KeyValuePairModelBinderTest.cs │ │ │ ├── KeyValuePairModelBinderUtilTest.cs │ │ │ ├── ModelBinderConfigTest.cs │ │ │ ├── ModelBinderProviderCollectionTest.cs │ │ │ ├── ModelBinderProvidersTest.cs │ │ │ ├── ModelBinderUtilTest.cs │ │ │ ├── ModelValidationNodeTest.cs │ │ │ ├── MutableObjectModelBinderProviderTest.cs │ │ │ ├── MutableObjectModelBinderTest.cs │ │ │ ├── SimpleModelBinderProviderTest.cs │ │ │ ├── TypeConverterModelBinderProviderTest.cs │ │ │ ├── TypeConverterModelBinderTest.cs │ │ │ ├── TypeMatchModelBinderProviderTest.cs │ │ │ └── TypeMatchModelBinderTest.cs │ │ ├── Test/ │ │ │ ├── AcceptAttributeTest.cs │ │ │ ├── AjaxOnlyAttributeTest.cs │ │ │ ├── AreaHelpersTest.cs │ │ │ ├── AsyncManagerExtensionsTest.cs │ │ │ ├── ButtonTest.cs │ │ │ ├── ContentTypeAttributeTest.cs │ │ │ ├── ControllerExtensionsTest.cs │ │ │ ├── CookieValueProviderFactoryTest.cs │ │ │ ├── CopyAsyncParametersAttributeTest.cs │ │ │ ├── CreditCardAttributeTest.cs │ │ │ ├── CssExtensionsTests.cs │ │ │ ├── DeserializeAttributeTest.cs │ │ │ ├── DynamicReflectionObjectTest.cs │ │ │ ├── DynamicViewDataDictionaryTest.cs │ │ │ ├── DynamicViewPageTest.cs │ │ │ ├── ElementalValueProviderTest.cs │ │ │ ├── EmailAddressAttribueTest.cs │ │ │ ├── ExpressionHelperTest.cs │ │ │ ├── FileExtensionsAttributeTest.cs │ │ │ ├── FormExtensionsTest.cs │ │ │ ├── ImageExtensionsTest.cs │ │ │ ├── MailToExtensionsTest.cs │ │ │ ├── ModelCopierTest.cs │ │ │ ├── MvcSerializerTest.cs │ │ │ ├── RadioExtensionsTest.cs │ │ │ ├── ReaderWriterCacheTest.cs │ │ │ ├── RenderActionTest.cs │ │ │ ├── ScriptExtensionsTest.cs │ │ │ ├── SerializationExtensionsTest.cs │ │ │ ├── ServerVariablesValueProviderFactoryTest.cs │ │ │ ├── SessionValueProviderFactoryTest.cs │ │ │ ├── SkipBindingAttributeTest.cs │ │ │ ├── SubmitButtonExtensionsTest.cs │ │ │ ├── SubmitImageExtensionsTest.cs │ │ │ ├── TempDataValueProviderFactoryTest.cs │ │ │ ├── TypeHelpersTest.cs │ │ │ ├── UrlAttributeTest.cs │ │ │ ├── ValueProviderUtilTest.cs │ │ │ └── VersionTest.cs │ │ └── packages.config │ ├── Microsoft.Web.WebPages.OAuth.Test/ │ │ ├── Microsoft.Web.WebPages.OAuth.Test.csproj │ │ ├── OAuthWebSecurityTest.cs │ │ ├── PreAppStartCodeTest.cs │ │ └── packages.config │ ├── Settings.StyleCop │ ├── System.Net.Http.Formatting.Test/ │ │ ├── ByteRangeStreamContentTest.cs │ │ ├── CustomMultipartFormDataRemoteStreamProvider.cs │ │ ├── DataSets/ │ │ │ ├── HttpTestData.cs │ │ │ └── Types/ │ │ │ ├── DataContractEnum.cs │ │ │ ├── DataContractType.cs │ │ │ ├── DerivedDataContractType.cs │ │ │ ├── DerivedFormUrlEncodedMediaTypeFormatter.cs │ │ │ ├── DerivedJsonMediaTypeFormatter.cs │ │ │ ├── DerivedWcfPocoType.cs │ │ │ ├── DerivedXmlMediaTypeFormatter.cs │ │ │ ├── DerivedXmlSerializableType.cs │ │ │ ├── INotJsonSerializable.cs │ │ │ ├── WcfPocoType.cs │ │ │ └── XmlSerializableType.cs │ │ ├── Formatting/ │ │ │ ├── BsonMediaTypeFormatterTests.cs │ │ │ ├── BufferedMediaTypeFormatterTests.cs │ │ │ ├── ContentNegotiationResultTest.cs │ │ │ ├── DataContractJsonMediaTypeFormatterTests.cs │ │ │ ├── DefaultContentNegotiatorTests.cs │ │ │ ├── FormDataCollectionTests.cs │ │ │ ├── FormUrlEncodedFromContentTests.cs │ │ │ ├── FormUrlEncodedFromUriQueryTests.cs │ │ │ ├── FormUrlEncodedJsonTests.cs │ │ │ ├── FormUrlEncodedMediaTypeFormatterTests.cs │ │ │ ├── JsonMediaTypeFormatterTests.cs │ │ │ ├── JsonNetSerializationTest.cs │ │ │ ├── JsonNetValidationTest.cs │ │ │ ├── MediaTypeConstantsTests.cs │ │ │ ├── MediaTypeFormatterCollectionTests.cs │ │ │ ├── MediaTypeFormatterExtensionsTests.cs │ │ │ ├── MediaTypeFormatterMatchTest.cs │ │ │ ├── MediaTypeFormatterTestBase.cs │ │ │ ├── MediaTypeFormatterTests.cs │ │ │ ├── MediaTypeHeaderValueExtensionsTests.cs │ │ │ ├── MediaTypeMappingTests.cs │ │ │ ├── MediaTypeWithQualityHeaderValueComparerTests.cs │ │ │ ├── ParsedMediaTypeHeaderValueTests.cs │ │ │ ├── Parsers/ │ │ │ │ ├── FormUrlEncodedParserTests.cs │ │ │ │ ├── HttpRequestHeaderParserTests.cs │ │ │ │ ├── HttpRequestLineParserTests.cs │ │ │ │ ├── HttpResponseHeaderParserTests.cs │ │ │ │ ├── HttpStatusLineParserTests.cs │ │ │ │ ├── InternetMessageFormatHeaderParserTests.cs │ │ │ │ └── MimeMultipartParserTests.cs │ │ │ ├── QueryStringMappingTests.cs │ │ │ ├── RequestHeaderMappingTests.cs │ │ │ ├── SerializerConsistencyTests.cs │ │ │ ├── StringComparisonHelperTest.cs │ │ │ ├── StringWithQualityHeaderValueComparerTests.cs │ │ │ ├── XmlHttpRequestHeaderMappingTest.cs │ │ │ ├── XmlMediaTypeFormatterTests.cs │ │ │ └── XmlSerializerMediaTypeFormatterTests.cs │ │ ├── FormattingUtilitiesTests.cs │ │ ├── Handlers/ │ │ │ ├── HttpProgressEventArgsTest.cs │ │ │ ├── ProgressContentTest.cs │ │ │ ├── ProgressMessageHandlerTest.cs │ │ │ ├── ProgressStreamTest.cs │ │ │ └── ProgressWriteAsyncResultTest.cs │ │ ├── Headers/ │ │ │ ├── CookieHeaderValueTest.cs │ │ │ └── CookieStateTest.cs │ │ ├── HttpClientExtensionsTest.cs │ │ ├── HttpClientFactoryTest.cs │ │ ├── HttpContentExtensionsTest.cs │ │ ├── HttpContentFormDataExtensionsTest.cs │ │ ├── HttpContentMessageExtensionsTests.cs │ │ ├── HttpContentMultipartExtensionsTests.cs │ │ ├── HttpHeaderExtensionsTest.cs │ │ ├── HttpMessageContentTests.cs │ │ ├── HttpRequestHeadersExtensionsTest.cs │ │ ├── HttpRequestMessageCommonExtensionsTest.cs │ │ ├── HttpResponseHeadersExtensionsTest.cs │ │ ├── HttpUnsortedRequestTest.cs │ │ ├── HttpUnsortedResponseTest.cs │ │ ├── Internal/ │ │ │ ├── AsyncResultTest.cs │ │ │ ├── ByteRangeStreamTest.cs │ │ │ ├── ConcurrentDictionaryTests.cs │ │ │ ├── DelegatingStreamTest.cs │ │ │ ├── HttpValueCollectionTest.cs │ │ │ ├── NonClosingDelegatingStreamTest.cs │ │ │ └── TranscodingStreamTests.cs │ │ ├── InvalidByteRangeExceptionTest.cs │ │ ├── MimeBodyPartTest.cs │ │ ├── Mocks/ │ │ │ ├── MockAsyncCallback.cs │ │ │ ├── MockCompletedAsyncResult.cs │ │ │ ├── MockContentNegotiator.cs │ │ │ ├── MockDelegatingHandler.cs │ │ │ ├── MockDelegatingStream.cs │ │ │ ├── MockHttpContent.cs │ │ │ ├── MockMediaTypeFormatter.cs │ │ │ ├── MockMediaTypeMapping.cs │ │ │ ├── MockNonClosingDelegatingStream.cs │ │ │ ├── MockProgressEventHandler.cs │ │ │ └── TestableHttpMessageHandler.cs │ │ ├── MultipartFileDataTest.cs │ │ ├── MultipartFileStreamProviderTests.cs │ │ ├── MultipartFormDataRemoteStreamProviderTests.cs │ │ ├── MultipartFormDataStreamProviderTests.cs │ │ ├── MultipartMemoryStreamProviderTests.cs │ │ ├── MultipartRelatedStreamProviderTests.cs │ │ ├── MultipartRemoteFileDataTests.cs │ │ ├── MultipartStreamProviderTestBase.cs │ │ ├── ObjectContentOfTTests.cs │ │ ├── ObjectContentTests.cs │ │ ├── ParserData.cs │ │ ├── PushStreamContentTest.cs │ │ ├── RemoteStreamInfoTests.cs │ │ ├── System.Net.Http.Formatting.Test.csproj │ │ ├── UriExtensionsTests.cs │ │ ├── UriQueryDataSet.cs │ │ └── packages.config │ ├── System.Net.Http.Formatting.ns1_3.Test/ │ │ └── System.Net.Http.Formatting.ns1_3.Test.csproj │ ├── System.Net.Http.Formatting.ns2_0.Test/ │ │ └── System.Net.Http.Formatting.ns2_0.Test.csproj │ ├── System.Web.Cors.Test/ │ │ ├── CorsEngineTest.cs │ │ ├── CorsPolicyTest.cs │ │ ├── CorsRequestContextTest.cs │ │ ├── CorsResultTest.cs │ │ ├── System.Web.Cors.Test.csproj │ │ └── packages.config │ ├── System.Web.Helpers.Test/ │ │ ├── ChartTest.cs │ │ ├── ConversionUtilTest.cs │ │ ├── CryptoTest.cs │ │ ├── DynamicDictionary.cs │ │ ├── DynamicHelperTest.cs │ │ ├── DynamicWrapper.cs │ │ ├── HelperResultTest.cs │ │ ├── JsonTest.cs │ │ ├── ObjectInfoTest.cs │ │ ├── PreComputedGridDataSourceTest.cs │ │ ├── ServerInfoTest.cs │ │ ├── System.Web.Helpers.Test.csproj │ │ ├── TestFiles/ │ │ │ └── xhtml11-flat.dtd │ │ ├── WebCacheTest.cs │ │ ├── WebGridDataSourceTest.cs │ │ ├── WebGridTest.cs │ │ ├── WebImageTest.cs │ │ ├── WebMailTest.cs │ │ ├── XhtmlAssert.cs │ │ └── packages.config │ ├── System.Web.Http.Cors.Test/ │ │ ├── AttributeBasedPolicyProviderFactoryTest.cs │ │ ├── Controllers/ │ │ │ ├── DefaultController.cs │ │ │ ├── ExternalActionSelectorAttribute.cs │ │ │ ├── PerControllerConfigController.cs │ │ │ ├── SampleController.cs │ │ │ └── ThrowingController.cs │ │ ├── CorsHttpConfigurationExtensionsTest.cs │ │ ├── CorsHttpRequestMessageExtensionsTest.cs │ │ ├── CorsHttpResponseMessageExtensionsTest.cs │ │ ├── CorsMessageHandlerTest.cs │ │ ├── DisableCorsAttributeTest.cs │ │ ├── EnableCorsAttributeTest.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── System.Web.Http.Cors.Test.csproj │ │ ├── Tracing/ │ │ │ ├── CorsEngineTracerTest.cs │ │ │ ├── CorsPolicyProviderFactoryTracerTest.cs │ │ │ └── CorsPolicyProviderTracerTest.cs │ │ └── packages.config │ ├── System.Web.Http.Integration.Test/ │ │ ├── ApiExplorer/ │ │ │ ├── ApiExplorerSettingsTest.cs │ │ │ ├── AttributeRoutesTest.cs │ │ │ ├── Controllers/ │ │ │ │ ├── AmbiguousActionController.cs │ │ │ │ ├── AttributeRouteControllers.cs │ │ │ │ ├── DocumentationController.cs │ │ │ │ ├── HiddenActionController.cs │ │ │ │ ├── HiddenController.cs │ │ │ │ ├── ItemController.cs │ │ │ │ ├── OverloadsController.cs │ │ │ │ ├── ParameterSourceController.cs │ │ │ │ └── ResponseTypeController.cs │ │ │ ├── DocumentationProviders/ │ │ │ │ └── AttributeDocumentationProvider.cs │ │ │ ├── DocumentationTest.cs │ │ │ ├── Formatters/ │ │ │ │ └── ItemFormatter.cs │ │ │ ├── FormattersTest.cs │ │ │ ├── ParameterSourceTest.cs │ │ │ ├── ResponseTypeAttributeTest.cs │ │ │ ├── RouteConstraintsTest.cs │ │ │ └── RoutesTest.cs │ │ ├── ContentNegotiation/ │ │ │ ├── AcceptHeaderTests.cs │ │ │ ├── ConnegController.cs │ │ │ ├── ConnegItem.cs │ │ │ ├── ContentNegotiationTestBase.cs │ │ │ ├── CustomFormatterTests.cs │ │ │ ├── DefaultContentNegotiatorTests.cs │ │ │ └── HttpResponseReturnTests.cs │ │ ├── Controllers/ │ │ │ ├── ActionAttributesTest.cs │ │ │ ├── ActionReachabilityTest.cs │ │ │ ├── ApiControllerActionSelectorTest.cs │ │ │ ├── Apis/ │ │ │ │ ├── ActionAttributeTestController.cs │ │ │ │ ├── EnumParameterOverloadsController.cs │ │ │ │ ├── HeaderValueProviderFactory.cs │ │ │ │ ├── ParameterAttributeController.cs │ │ │ │ ├── ParameterTestController.cs │ │ │ │ ├── RegularConfigController.cs │ │ │ │ ├── SpecialConfigController.cs │ │ │ │ ├── TestController.cs │ │ │ │ ├── User.cs │ │ │ │ ├── UserAddress.cs │ │ │ │ ├── UsersController.cs │ │ │ │ └── ValuesController.cs │ │ │ ├── ControllerConfigurationTest.cs │ │ │ └── Helpers/ │ │ │ └── ApiControllerHelper.cs │ │ ├── Dispatcher/ │ │ │ └── CustomHttpControllerTypeResolverTest.cs │ │ ├── ExceptionHandling/ │ │ │ ├── DuplicateControllers.cs │ │ │ ├── ExceptionController.cs │ │ │ ├── ExceptionHandlingTest.cs │ │ │ ├── HttpResponseExceptionTest.cs │ │ │ └── IncludeErrorDetailTest.cs │ │ ├── ModelBinding/ │ │ │ ├── BodyBindingTests.cs │ │ │ ├── CustomBindingTests.cs │ │ │ ├── DefaultActionValueBinderTest.cs │ │ │ ├── HttpContentBindingTests.cs │ │ │ ├── ModelBindingController.cs │ │ │ ├── ModelBindingTests.cs │ │ │ ├── QueryStringBindingTests.cs │ │ │ └── RouteBindingTests.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── System.Web.Http.Integration.Test.csproj │ │ ├── Tracing/ │ │ │ ├── ITestTraceWriter.cs │ │ │ ├── MemoryTraceWriter.cs │ │ │ ├── NeverTracesTraceWriter.cs │ │ │ └── TracingTest.cs │ │ ├── Util/ │ │ │ ├── ApiExplorerHelper.cs │ │ │ ├── ContextUtil.cs │ │ │ ├── ConvertToStreamMessageHandler.cs │ │ │ ├── HttpServerTestBase.cs │ │ │ └── ScenarioHelper.cs │ │ └── packages.config │ ├── System.Web.Http.Owin.Test/ │ │ ├── ExceptionHandling/ │ │ │ ├── DefaultExceptionHandlerTests.cs │ │ │ └── EmptyExceptionLoggerTests.cs │ │ ├── HostAuthenticationAttributeTest.cs │ │ ├── HostAuthenticationFilterTest.cs │ │ ├── HttpMessageHandlerAdapterTest.cs │ │ ├── HttpMessageHandlerExtensions.cs │ │ ├── HttpMessageHandlerOptionsTests.cs │ │ ├── IgnoreRouteTest.cs │ │ ├── NonOwnedStreamTests.cs │ │ ├── OwinBufferPolicySelectorTest.cs │ │ ├── OwinExceptionCatchBlocksTests.cs │ │ ├── OwinHostIntegrationTest.cs │ │ ├── OwinHttpConfigurationExtensionsTest.cs │ │ ├── OwinHttpRequestContextTests.cs │ │ ├── OwinHttpRequestMessageExtensionsTest.cs │ │ ├── OwinRequestExtensionsTests.cs │ │ ├── OwinResponseExtensionsTests.cs │ │ ├── PassiveAuthenticationMessageHandlerTest.cs │ │ ├── System.Web.Http.Owin.Test.csproj │ │ ├── WebApiAppBuilderExtensionsTest.cs │ │ └── packages.config │ ├── System.Web.Http.SelfHost.Test/ │ │ ├── Authentication/ │ │ │ ├── BasicOverHttpTest.cs │ │ │ ├── CustomMessageHandler.cs │ │ │ ├── CustomUsernamePasswordValidator.cs │ │ │ ├── RequireAdminAttribute.cs │ │ │ └── SampleController.cs │ │ ├── Channels/ │ │ │ └── HttpBindingSecurityModeHelperTest.cs │ │ ├── DeeplyNestedTypeTests.cs │ │ ├── HttpSelfHostConfigurationTest.cs │ │ ├── HttpSelfHostResponseTest.cs │ │ ├── HttpSelfHostServerTest.cs │ │ ├── MaxHttpCollectionKeyTests.cs │ │ ├── SelfHostHttpRequestContextTests.cs │ │ ├── ServiceModel/ │ │ │ ├── HostNameComparisonModeHelperTest.cs │ │ │ └── TransferModeHelperTest.cs │ │ ├── System.Web.Http.SelfHost.Test.csproj │ │ └── packages.config │ ├── System.Web.Http.SignalR.Test/ │ │ ├── HubControllerBaseTest.cs │ │ ├── HubControllerOfTHubTest.cs │ │ ├── HubControllerTest.cs │ │ ├── System.Web.Http.SignalR.Test.csproj │ │ └── packages.config │ ├── System.Web.Http.Test/ │ │ ├── AuthorizeAttributeTest.cs │ │ ├── Batch/ │ │ │ ├── BatchHttpRequestContextTests.cs │ │ │ ├── BatchHttpRequestMessageExtensionsTest.cs │ │ │ ├── BatchLearningTests.cs │ │ │ ├── DefaultHttpBatchHandlerTest.cs │ │ │ └── HttpBatchHandlerTest.cs │ │ ├── Common/ │ │ │ └── TraceWriterExceptionMapperTest.cs │ │ ├── Controllers/ │ │ │ ├── ActionFilterResultTests.cs │ │ │ ├── ApiControllerActionInvokerTest.cs │ │ │ ├── ApiControllerActionSelectorTest.cs │ │ │ ├── ApiControllerTest.cs │ │ │ ├── ApiControllerTestabilityTest.cs │ │ │ ├── Apis/ │ │ │ │ ├── User.cs │ │ │ │ ├── UsersController.cs │ │ │ │ └── UsersRpcController.cs │ │ │ ├── AuthenticationFilterResultTests.cs │ │ │ ├── AuthorizationFilterResultTests.cs │ │ │ ├── ExceptionFilterResultTests.cs │ │ │ ├── FilterGroupingTests.cs │ │ │ ├── HttpActionContextTest.cs │ │ │ ├── HttpActionDescriptorTest.cs │ │ │ ├── HttpConfigurationTest.cs │ │ │ ├── HttpControllerContextTest.cs │ │ │ ├── HttpControllerDescriptorTest.cs │ │ │ ├── HttpParameterBindingTest.cs │ │ │ ├── HttpParameterDescriptorTest.cs │ │ │ ├── ParameterBindingExtensionsTest.cs │ │ │ ├── ReflectedHttpActionDescriptorTest.cs │ │ │ ├── ReflectedHttpParameterDescriptorTest.cs │ │ │ ├── RequestBackedHttpRequestContextTests.cs │ │ │ ├── ResponseMessageResultConverterTest.cs │ │ │ ├── ValueResultConverterTest.cs │ │ │ └── VoidResultConverterTest.cs │ │ ├── Description/ │ │ │ ├── ApiExplorerTest.cs │ │ │ └── ApiParameterDescriptionTest.cs │ │ ├── Dispatcher/ │ │ │ ├── DefaultAssembliesResolverTest.cs │ │ │ ├── DefaultHttpControllerActivatorTest.cs │ │ │ ├── DefaultHttpControllerSelectorTest.cs │ │ │ ├── DefaultHttpControllerTypeResolverTest.cs │ │ │ ├── HttpControllerDispatcherTest.cs │ │ │ ├── HttpErrorTest.cs │ │ │ └── HttpRoutingDispatcherTest.cs │ │ ├── ExceptionHandling/ │ │ │ ├── CompositeExceptionLoggerTests.cs │ │ │ ├── DefaultExceptionHandlerTests.cs │ │ │ ├── EmptyExceptionHandlerTests.cs │ │ │ ├── ExceptionCatchBlocksTests.cs │ │ │ ├── ExceptionContextCatchBlockTests.cs │ │ │ ├── ExceptionContextTests.cs │ │ │ ├── ExceptionHandlerContextTests.cs │ │ │ ├── ExceptionHandlerExtensionsTests.cs │ │ │ ├── ExceptionHandlerTests.cs │ │ │ ├── ExceptionLoggerContextTests.cs │ │ │ ├── ExceptionLoggerExtensionsTests.cs │ │ │ ├── ExceptionLoggerTests.cs │ │ │ ├── ExceptionServicesTests.cs │ │ │ └── LastChanceExceptionHandlerTests.cs │ │ ├── Filters/ │ │ │ ├── ActionDescriptorFilterProviderTest.cs │ │ │ ├── ActionFilterAttributeTest.cs │ │ │ ├── AuthorizationFilterAttributeTest.cs │ │ │ ├── ConfigurationFilterProviderTest.cs │ │ │ ├── ExceptionFilterAttributeTest.cs │ │ │ ├── FilterAttributeTest.cs │ │ │ ├── FilterInfoComparerTest.cs │ │ │ ├── FilterInfoTest.cs │ │ │ ├── HttpActionExecutedContextTest.cs │ │ │ ├── HttpAuthenticationChallengeContextTests.cs │ │ │ ├── HttpAuthenticationContextTests.cs │ │ │ └── HttpFilterCollectionTest.cs │ │ ├── Hosting/ │ │ │ ├── HttpMessageHandlerExtensions.cs │ │ │ ├── HttpRouteTest.cs │ │ │ └── SuppressHostPrincipalMessageHandlerTest.cs │ │ ├── HttpConfigurationExtensionsTest.cs │ │ ├── HttpErrorKeysTest.cs │ │ ├── HttpMessageHandlerExtensions.cs │ │ ├── HttpRequestMessageExtensionsTest.cs │ │ ├── HttpResponseExceptionTest.cs │ │ ├── HttpResponseMessageExtensionsTest.cs │ │ ├── HttpRouteCollectionExtensionsTest.cs │ │ ├── HttpRouteCollectionTest.cs │ │ ├── HttpServerTest.cs │ │ ├── Internal/ │ │ │ ├── CollectionModelBinderUtilTest.cs │ │ │ └── TypeActivatorTest.cs │ │ ├── Metadata/ │ │ │ ├── ModelMetadataTest.cs │ │ │ └── Providers/ │ │ │ ├── AssociatedMetadataProviderTest.cs │ │ │ └── DataAnnotationsModelMetadataProviderTest.cs │ │ ├── ModelBinding/ │ │ │ ├── Binders/ │ │ │ │ ├── ArrayModelBinderProviderTest.cs │ │ │ │ ├── ArrayModelBinderTest.cs │ │ │ │ ├── CollectionModelBinderProviderTest.cs │ │ │ │ ├── CollectionModelBinderTest.cs │ │ │ │ ├── ComplexModelDtoModelBinderProviderTest.cs │ │ │ │ ├── ComplexModelDtoModelBinderTest.cs │ │ │ │ ├── ComplexModelDtoResultTest.cs │ │ │ │ ├── ComplexModelDtoTest.cs │ │ │ │ ├── DictionaryModelBinderProviderTest.cs │ │ │ │ ├── DictionaryModelBinderTest.cs │ │ │ │ ├── KeyValuePairModelBinderProviderTest.cs │ │ │ │ ├── KeyValuePairModelBinderTest.cs │ │ │ │ ├── KeyValuePairModelBinderUtilTest.cs │ │ │ │ ├── MutableObjectModelBinderProviderTest.cs │ │ │ │ ├── MutableObjectModelBinderTest.cs │ │ │ │ ├── SimpleModelBinderProviderTest.cs │ │ │ │ ├── TypeConverterModelBinderProviderTest.cs │ │ │ │ ├── TypeConverterModelBinderTest.cs │ │ │ │ ├── TypeMatchModelBinderProviderTest.cs │ │ │ │ └── TypeMatchModelBinderTest.cs │ │ │ ├── CompositeModelBinderTest.cs │ │ │ ├── DefaultActionValueBinderTest.cs │ │ │ ├── FormDataCollectionExtensionsTest.cs │ │ │ ├── FormatterParameterBindingTest.cs │ │ │ ├── HttpBindingBehaviorAttributeTest.cs │ │ │ ├── HttpParameterBindingExtensionsTest.cs │ │ │ ├── ModelBinderAttributeTest.cs │ │ │ ├── ModelBinderConfigTest.cs │ │ │ ├── ModelBindingContextTest.cs │ │ │ ├── ModelBindingEndToEndTests.cs │ │ │ ├── ModelBindingUtilTest.cs │ │ │ ├── ParameterBindingProvidersTest.cs │ │ │ └── SharedModels/ │ │ │ ├── Address.cs │ │ │ ├── PeopleModel.cs │ │ │ ├── Person.cs │ │ │ ├── StreetAddress.cs │ │ │ └── User.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Results/ │ │ │ ├── BadRequestErrorMessageResultTests.cs │ │ │ ├── BadRequestResultTests.cs │ │ │ ├── ConflictResultTests.cs │ │ │ ├── CreatedAtRouteNegotiatedContentResultTests.cs │ │ │ ├── CreatedNegotiatedContentResultTests.cs │ │ │ ├── ExceptionResultTests.cs │ │ │ ├── FormattedContentResultTests.cs │ │ │ ├── InternalServerErrorResultTests.cs │ │ │ ├── InvalidModelStateResultTests.cs │ │ │ ├── JsonResultTests.cs │ │ │ ├── NegotiatedContentResultTests.cs │ │ │ ├── NotFoundResultTests.cs │ │ │ ├── OkNegotiatedContentResultTests.cs │ │ │ ├── OkResultTests.cs │ │ │ ├── RedirectResultTests.cs │ │ │ ├── RedirectToRouteResultTests.cs │ │ │ ├── ResponseMessageResultTests.cs │ │ │ ├── StatusCodeResultTests.cs │ │ │ └── UnauthorizedResultTests.cs │ │ ├── Routing/ │ │ │ ├── AttributeRoutingTest.cs │ │ │ ├── DefaultDirectRouteProviderTests.cs │ │ │ ├── DirectRouteProviderContextTests.cs │ │ │ ├── HttpRouteTest.cs │ │ │ ├── HttpRouteValueDictionaryTest.cs │ │ │ ├── LinkGenerationRouteTests.cs │ │ │ ├── MediaTypeFormatterExtensionsTests.cs │ │ │ ├── RouteAttributeTests.cs │ │ │ ├── UriPathExtensionMappingTests.cs │ │ │ └── UrlHelperTest.cs │ │ ├── Services/ │ │ │ ├── ControllerServicesTests.cs │ │ │ ├── DecoratorTests.cs │ │ │ ├── DefaultServicesTests.cs │ │ │ └── ServicesExtensionsTests.cs │ │ ├── System.Web.Http.Test.csproj │ │ ├── Tracing/ │ │ │ ├── FormattingUtilitiesTest.cs │ │ │ ├── HttpRequestMessageExtensionsTest.cs │ │ │ ├── ITraceWriterExtensionsTest.cs │ │ │ ├── TestTraceWriter.cs │ │ │ ├── TraceKindHelperTest.cs │ │ │ ├── TraceLevelHelperTest.cs │ │ │ ├── TraceManagerTest.cs │ │ │ ├── TraceRecordComparer.cs │ │ │ ├── TraceRecordTest.cs │ │ │ ├── TracerCorrectnessTest.cs │ │ │ └── Tracers/ │ │ │ ├── ActionFilterAttributeTracerTest.cs │ │ │ ├── ActionFilterTracerTest.cs │ │ │ ├── ActionValueBinderTracerTest.cs │ │ │ ├── AuthenticationFilterTracerTests.cs │ │ │ ├── AuthorizationFilterAttributeTracerTest.cs │ │ │ ├── AuthorizationFilterTracerTest.cs │ │ │ ├── BufferedMediaTypeFormatterTracerTest.cs │ │ │ ├── ContentNegotiatorTracerTest.cs │ │ │ ├── DefaultHttpControllerTypeResolverTracerTest.cs │ │ │ ├── ExceptionFilterAttributeTracerTest.cs │ │ │ ├── ExceptionFilterTracerTest.cs │ │ │ ├── FilterTracerTest.cs │ │ │ ├── FormUrlEncodedMediaTypeFormatterTracerTest.cs │ │ │ ├── FormatterParameterBindingTracerTest.cs │ │ │ ├── HttpActionBindingTracerTest.cs │ │ │ ├── HttpActionDescriptorTracerTest.cs │ │ │ ├── HttpActionInvokerTracerTest.cs │ │ │ ├── HttpActionSelectorTracerTest.cs │ │ │ ├── HttpControllerActivatorTracerTest.cs │ │ │ ├── HttpControllerDescriptorTracerTest.cs │ │ │ ├── HttpControllerSelectorTracerTest.cs │ │ │ ├── HttpControllerTracerTest.cs │ │ │ ├── HttpParameterBindingTracerTest.cs │ │ │ ├── JsonMediaTypeFormatterTracerTest.cs │ │ │ ├── MediaTypeFormatterTracerTest.cs │ │ │ ├── MediaTypeFormatterTracerTestBase.cs │ │ │ ├── MessageHandlerTracerTest.cs │ │ │ ├── OverrideFilterTracerTests.cs │ │ │ ├── ReadWriteMediaTypeFormatterTracerTestBase.cs │ │ │ ├── RequestMessageHandlerTracerTest.cs │ │ │ └── XmlMediaTypeFormatterTracerTest.cs │ │ ├── Util/ │ │ │ ├── ContextUtil.cs │ │ │ └── SimpleHttpValueProvider.cs │ │ ├── Validation/ │ │ │ ├── DefaultBodyModelValidatorTest.cs │ │ │ ├── ModelStateFormatterLoggerTest.cs │ │ │ ├── ModelValidationNodeTest.cs │ │ │ ├── ModelValidationRequiredMemberSelectorTest.cs │ │ │ ├── ModelValidationResultTest.cs │ │ │ ├── ModelValidatorTest.cs │ │ │ ├── Providers/ │ │ │ │ ├── AssociatedValidatorProviderTest.cs │ │ │ │ ├── DataAnnotationsModelValidatorProviderTest.cs │ │ │ │ ├── DataMemberModelValidatorProviderTest.cs │ │ │ │ └── InvalidModelValidatorProviderTest.cs │ │ │ ├── ReferenceEqualityComparerTest.cs │ │ │ └── Validators/ │ │ │ ├── DataAnnotationsModelValidatorTest.cs │ │ │ └── ErrorModelValidatorTest.cs │ │ ├── ValueProviders/ │ │ │ ├── Providers/ │ │ │ │ ├── ElementalValueProviderTest.cs │ │ │ │ ├── NameValuePairsValueProviderTest.cs │ │ │ │ ├── QueryStringValueProviderFactoryTest.cs │ │ │ │ ├── QueryStringValueProviderTest.cs │ │ │ │ ├── RouteDataValueProviderFactoryTest.cs │ │ │ │ └── RouteDataValueProviderTest.cs │ │ │ └── ValueProviderResultTest.cs │ │ └── packages.config │ ├── System.Web.Http.Tracing.Test/ │ │ ├── HttpConfigurationTracingExtensionsTest.cs │ │ ├── System.Web.Http.Tracing.Test.csproj │ │ ├── SystemDiagnosticsTraceWriterTest.cs │ │ └── packages.config │ ├── System.Web.Http.WebHost.Test/ │ │ ├── BatchingTest.cs │ │ ├── GlobalConfigurationTest.cs │ │ ├── HttpControllerHandlerTest.cs │ │ ├── HttpRequestMessageExtensions.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RouteCollectionExtensionsTest.cs │ │ ├── Routing/ │ │ │ ├── HostedHttpRouteCollectionTest.cs │ │ │ ├── HostedUrlHelperTest.cs │ │ │ ├── HttpContextBaseExtensionsTest.cs │ │ │ ├── HttpRequestMessageWrapperTest.cs │ │ │ ├── HttpRouteExceptionHandlerTests.cs │ │ │ ├── HttpRouteExceptionRouteHandlerTests.cs │ │ │ └── HttpWebRouteTests.cs │ │ ├── SeekableBufferedRequestStreamTest.cs │ │ ├── SuppressFormsAuthRedirectHelperTest.cs │ │ ├── System.Web.Http.WebHost.Test.csproj │ │ ├── WebHostBufferPolicySelectorTest.cs │ │ ├── WebHostExceptionCatchBlocksTests.cs │ │ ├── WebHostExceptionHandlerTests.cs │ │ ├── WebHostHttpRequestContextTests.cs │ │ └── packages.config │ ├── System.Web.Mvc.Test/ │ │ ├── Ajax/ │ │ │ └── Test/ │ │ │ ├── AjaxExtensionsTest.cs │ │ │ └── AjaxOptionsTest.cs │ │ ├── Async/ │ │ │ └── Test/ │ │ │ ├── AsyncActionDescriptorTest.cs │ │ │ ├── AsyncActionMethodSelectorTest.cs │ │ │ ├── AsyncControllerActionInvokerTest.cs │ │ │ ├── AsyncManagerTest.cs │ │ │ ├── AsyncResultWrapperTest.cs │ │ │ ├── MockAsyncResult.cs │ │ │ ├── OperationCounterTest.cs │ │ │ ├── ReflectedAsyncActionDescriptorTest.cs │ │ │ ├── ReflectedAsyncControllerDescriptorTest.cs │ │ │ ├── SignalContainer.cs │ │ │ ├── SimpleAsyncResultTest.cs │ │ │ ├── SingleEntryGateTest.cs │ │ │ ├── SynchronizationContextUtilTest.cs │ │ │ ├── SynchronousOperationExceptionTest.cs │ │ │ ├── TaskAsyncActionDescriptorTest.cs │ │ │ ├── TaskWrapperAsyncResultTest.cs │ │ │ └── TriggerListenerTest.cs │ │ ├── ExpressionUtil/ │ │ │ └── Test/ │ │ │ ├── BinaryExpressionFingerprintTest.cs │ │ │ ├── CachedExpressionCompilerTest.cs │ │ │ ├── ConditionalExpressionFingerprintTest.cs │ │ │ ├── ConstantExpressionFingerprintTest.cs │ │ │ ├── DefaultExpressionFingerprintTest.cs │ │ │ ├── DummyExpressionFingerprint.cs │ │ │ ├── ExpressionFingerprintTest.cs │ │ │ ├── FingerprintingExpressionVisitorTest.cs │ │ │ ├── HoistingExpressionVisitorTest.cs │ │ │ ├── IndexExpressionFingerprintTest.cs │ │ │ ├── LambdaExpressionFingerprintTest.cs │ │ │ ├── MemberExpressionFingerprintTest.cs │ │ │ ├── MethodCallExpressionFingerprintTest.cs │ │ │ ├── ParameterExpressionFingerprintTest.cs │ │ │ ├── TypeBinaryExpressionFingerprintTest.cs │ │ │ └── UnaryExpressionFingerprintTest.cs │ │ ├── Html/ │ │ │ └── Test/ │ │ │ ├── ChildActionExtensionsTest.cs │ │ │ ├── DefaultDisplayTemplatesTest.cs │ │ │ ├── DefaultEditorTemplatesTest.cs │ │ │ ├── DisplayExtensionsTest.cs │ │ │ ├── DisplayNameExtensionsTest.cs │ │ │ ├── DisplayTextExtensionsTest.cs │ │ │ ├── EditorExtensionsTest.cs │ │ │ ├── EncodedDataSets.cs │ │ │ ├── EnumHelperTest.cs │ │ │ ├── FormExtensionsTest.cs │ │ │ ├── InputExtensionsTest.cs │ │ │ ├── LabelExtensionsTest.cs │ │ │ ├── LinkExtensionsTest.cs │ │ │ ├── MetadataOverrideScope.cs │ │ │ ├── MvcFormTest.cs │ │ │ ├── NameExtensionsTest.cs │ │ │ ├── PartialExtensionsTest.cs │ │ │ ├── RenderPartialExtensionsTest.cs │ │ │ ├── SelectExtensionsTest.cs │ │ │ ├── TemplateHelpersSafeScope.cs │ │ │ ├── TemplateHelpersTest.cs │ │ │ ├── TextAreaExtensionsTest.cs │ │ │ ├── ValidationExtensionsTest.cs │ │ │ └── ValueExtensionsTest.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ └── Resources.resx │ │ ├── Razor/ │ │ │ └── Test/ │ │ │ ├── MvcCSharpRazorCodeGeneratorTest.cs │ │ │ ├── MvcCSharpRazorCodeParserTest.cs │ │ │ ├── MvcVBRazorCodeParserTest.cs │ │ │ └── MvcWebPageRazorHostTest.cs │ │ ├── Routing/ │ │ │ ├── AttributeRoutingLinkGenerationTest.cs │ │ │ ├── AttributeRoutingMapperTest.cs │ │ │ ├── AttributeRoutingTest.cs │ │ │ ├── DefaultDirectRouteProviderTest.cs │ │ │ ├── DirectRouteCandidateTest.cs │ │ │ ├── DirectRouteTestHelpers.cs │ │ │ ├── RouteAttributeTests.cs │ │ │ └── RouteCollectionAttributeRoutingExtensionsTests.cs │ │ ├── System.Web.Mvc.Test.csproj │ │ ├── Test/ │ │ │ ├── AcceptVerbsAttributeTest.cs │ │ │ ├── ActionDescriptorTest.cs │ │ │ ├── ActionExecutedContextTest.cs │ │ │ ├── ActionExecutingContextTest.cs │ │ │ ├── ActionFilterAttributeTest.cs │ │ │ ├── ActionMethodDispatcherCacheTest.cs │ │ │ ├── ActionMethodDispatcherTest.cs │ │ │ ├── ActionMethodSelectorTest.cs │ │ │ ├── ActionNameAttributeTest.cs │ │ │ ├── AdditionalMetadataAttributeTest.cs │ │ │ ├── AjaxHelperOfTModelTest.cs │ │ │ ├── AjaxHelperTest.cs │ │ │ ├── AjaxRequestExtensionsTest.cs │ │ │ ├── AllowHtmlAttributeTest.cs │ │ │ ├── AreaHelpersTest.cs │ │ │ ├── AreaRegistrationContextTest.cs │ │ │ ├── AreaRegistrationTest.cs │ │ │ ├── AssociatedMetadataProviderTest.cs │ │ │ ├── AssociatedValidatorProviderTest.cs │ │ │ ├── AsyncControllerTest.cs │ │ │ ├── AsyncTimeoutAttributeTest.cs │ │ │ ├── AuthorizationContextTest.cs │ │ │ ├── AuthorizeAttributeTest.cs │ │ │ ├── BindAttributeTest.cs │ │ │ ├── BuildManagerCompiledViewTest.cs │ │ │ ├── BuildManagerViewEngineTest.cs │ │ │ ├── ByteArrayModelBinderTest.cs │ │ │ ├── CachedAssociatedMetadataProviderTest.cs │ │ │ ├── CachedDataAnnotationsModelMetadataProviderTest.cs │ │ │ ├── CancellationTokenModelBinderTest.cs │ │ │ ├── ChildActionOnlyAttributeTest.cs │ │ │ ├── ChildActionValueProviderFactoryTest.cs │ │ │ ├── ClientDataTypeModelValidatorProviderTest.cs │ │ │ ├── CompareAttributeAdapterTest.cs │ │ │ ├── CompareAttributeTest.cs │ │ │ ├── ContentResultTest.cs │ │ │ ├── ControllerActionInvokerTest.cs │ │ │ ├── ControllerBaseTest.cs │ │ │ ├── ControllerBuilderTest.cs │ │ │ ├── ControllerContextTest.cs │ │ │ ├── ControllerDescriptorCacheTest.cs │ │ │ ├── ControllerDescriptorTest.cs │ │ │ ├── ControllerInstanceFilterProviderTest.cs │ │ │ ├── ControllerTest.cs │ │ │ ├── CopyOnWriteDictionaryTest.cs │ │ │ ├── DataAnnotationsModelMetadataProviderTest.cs │ │ │ ├── DataAnnotationsModelMetadataProviderTestBase.cs │ │ │ ├── DataAnnotationsModelValidatorProviderTest.cs │ │ │ ├── DataAnnotationsModelValidatorTest.cs │ │ │ ├── DataErrorInfoModelValidatorProviderTest.cs │ │ │ ├── DataTypeUtilTest.cs │ │ │ ├── DefaultControllerFactoryTest.cs │ │ │ ├── DefaultModelBinderTest.cs │ │ │ ├── DefaultViewLocationCacheTest.cs │ │ │ ├── DependencyResolverTest.cs │ │ │ ├── DescriptorUtilTest.cs │ │ │ ├── DictionaryHelpersTest.cs │ │ │ ├── DictionaryValueProviderTest.cs │ │ │ ├── DynamicViewDataDictionaryTest.cs │ │ │ ├── EmptyModelValidatorProviderTest.cs │ │ │ ├── ExceptionContextTest.cs │ │ │ ├── ExpressionHelperTest.cs │ │ │ ├── FieldValidationMetadataTest.cs │ │ │ ├── FileContentResultTest.cs │ │ │ ├── FilePathResultTest.cs │ │ │ ├── FileResultTest.cs │ │ │ ├── FileStreamResultTest.cs │ │ │ ├── FilterAttributeFilterProviderTest.cs │ │ │ ├── FilterInfoTest.cs │ │ │ ├── FilterProviderCollectionTest.cs │ │ │ ├── FilterProvidersTest.cs │ │ │ ├── FilterTest.cs │ │ │ ├── FormCollectionTest.cs │ │ │ ├── FormContextTest.cs │ │ │ ├── FormValueProviderFactoryTest.cs │ │ │ ├── GlobalFilterCollectionTest.cs │ │ │ ├── HandleErrorAttributeTest.cs │ │ │ ├── HandleErrorInfoTest.cs │ │ │ ├── HtmlHelperOfTModelTest.cs │ │ │ ├── HtmlHelperTest.cs │ │ │ ├── HttpDeleteAttributeTest.cs │ │ │ ├── HttpFileCollectionValueProviderFactoryTest.cs │ │ │ ├── HttpFileCollectionValueProviderTest.cs │ │ │ ├── HttpGetAttributeTest.cs │ │ │ ├── HttpHandlerUtilTest.cs │ │ │ ├── HttpHeadAttributeTest.cs │ │ │ ├── HttpNotFoundResultTest.cs │ │ │ ├── HttpOptionsAttributeTest.cs │ │ │ ├── HttpPatchAttributeTest.cs │ │ │ ├── HttpPostAttributeTest.cs │ │ │ ├── HttpPostedFileBaseModelBinderTest.cs │ │ │ ├── HttpPutAttributeTest.cs │ │ │ ├── HttpRequestExtensionsTest.cs │ │ │ ├── HttpStatusCodeResultTest.cs │ │ │ ├── HttpUnauthorizedResultTest.cs │ │ │ ├── HttpVerbAttributeHelper.cs │ │ │ ├── JQueryFormValueProviderFactoryTest.cs │ │ │ ├── JavaScriptResultTest.cs │ │ │ ├── JsonResultTest.cs │ │ │ ├── JsonValueProviderFactoryTest.cs │ │ │ ├── LinqBinaryModelBinderTest.cs │ │ │ ├── MaxLengthAttributeAdapterTest.cs │ │ │ ├── MinLengthAttributeAdapterTest.cs │ │ │ ├── MockBuildManager.cs │ │ │ ├── MockHelpers.cs │ │ │ ├── MockableUnvalidatedRequestValues.cs │ │ │ ├── ModelBinderAttributeTest.cs │ │ │ ├── ModelBinderDictionaryTest.cs │ │ │ ├── ModelBinderProviderCollectionTest.cs │ │ │ ├── ModelBinderProvidersTest.cs │ │ │ ├── ModelBindersTest.cs │ │ │ ├── ModelBindingContextTest.cs │ │ │ ├── ModelClientValidationRuleTest.cs │ │ │ ├── ModelErrorCollectionTest.cs │ │ │ ├── ModelErrorTest.cs │ │ │ ├── ModelMetadataProvidersTest.cs │ │ │ ├── ModelMetadataTest.cs │ │ │ ├── ModelStateDictionaryTest.cs │ │ │ ├── ModelStateTest.cs │ │ │ ├── ModelValidationResultTest.cs │ │ │ ├── ModelValidatorProviderCollectionTest.cs │ │ │ ├── ModelValidatorProvidersTest.cs │ │ │ ├── ModelValidatorTest.cs │ │ │ ├── MultiSelectListTest.cs │ │ │ ├── MultiServiceResolverTest.cs │ │ │ ├── MvcHandlerTest.cs │ │ │ ├── MvcHtmlStringTest.cs │ │ │ ├── MvcHttpHandlerTest.cs │ │ │ ├── MvcRouteHandlerTest.cs │ │ │ ├── MvcTestHelper.cs │ │ │ ├── MvcWebRazorHostFactoryTest.cs │ │ │ ├── NameValueCollectionExtensionsTest.cs │ │ │ ├── NameValueCollectionValueProviderTest.cs │ │ │ ├── NoAsyncTimeoutAttributeTest.cs │ │ │ ├── NonActionAttributeTest.cs │ │ │ ├── OutputCacheAttributeTest.cs │ │ │ ├── OverrideActionFiltersAttributeTests.cs │ │ │ ├── OverrideAuthenticationAttributeTests.cs │ │ │ ├── OverrideAuthorizationAttributeTests.cs │ │ │ ├── OverrideExceptionFiltersAttributeTests.cs │ │ │ ├── OverrideFiltersAttributeTests.cs │ │ │ ├── OverrideResultFiltersAttributeTests.cs │ │ │ ├── ParameterBindingInfoTest.cs │ │ │ ├── ParameterDescriptorTest.cs │ │ │ ├── ParameterInfoUtilTest.cs │ │ │ ├── PartialViewResultTest.cs │ │ │ ├── PreApplicationStartCodeTest.cs │ │ │ ├── QueryStringValueProviderFactoryTest.cs │ │ │ ├── RangeAttributeAdapterTest.cs │ │ │ ├── RazorViewEngineTest.cs │ │ │ ├── RazorViewTest.cs │ │ │ ├── ReaderWriterCacheTest.cs │ │ │ ├── RedirectResultTest.cs │ │ │ ├── RedirectToRouteResultTest.cs │ │ │ ├── ReflectedActionDescriptorTest.cs │ │ │ ├── ReflectedControllerDescriptorTest.cs │ │ │ ├── ReflectedParameterBindingInfoTest.cs │ │ │ ├── ReflectedParameterDescriptorTest.cs │ │ │ ├── RegularExpressionAttributeAdapterTest.cs │ │ │ ├── RemoteAttributeTest.cs │ │ │ ├── RequireHttpsAttributeTest.cs │ │ │ ├── RequiredAttributeAdapterTest.cs │ │ │ ├── ResultExecutedContextTest.cs │ │ │ ├── ResultExecutingContextTest.cs │ │ │ ├── RouteCollectionExtensionsTest.cs │ │ │ ├── RouteDataValueProviderFactoryTest.cs │ │ │ ├── SelectListTest.cs │ │ │ ├── SessionStateTempDataProviderTest.cs │ │ │ ├── SingleServiceResolverTest.cs │ │ │ ├── StringLengthAttributeAdapterTest.cs │ │ │ ├── TempDataDictionaryTest.cs │ │ │ ├── TypeCacheSerializerTest.cs │ │ │ ├── TypeCacheUtilTest.cs │ │ │ ├── TypeHelpersTest.cs │ │ │ ├── UrlHelperTest.cs │ │ │ ├── UrlParameterTest.cs │ │ │ ├── ValidatableObjectAdapterTest.cs │ │ │ ├── ValidateAntiForgeryTokenAttributeTest.cs │ │ │ ├── ValidateInputAttributeTest.cs │ │ │ ├── ValueProviderCollectionTest.cs │ │ │ ├── ValueProviderDictionaryTest.cs │ │ │ ├── ValueProviderFactoriesTest.cs │ │ │ ├── ValueProviderFactoryCollectionTest.cs │ │ │ ├── ValueProviderResultTest.cs │ │ │ ├── ValueProviderUtilTest.cs │ │ │ ├── ViewContextTest.cs │ │ │ ├── ViewDataDictionaryTest.cs │ │ │ ├── ViewDataInfoTest.cs │ │ │ ├── ViewEngineCollectionTest.cs │ │ │ ├── ViewEngineResultTest.cs │ │ │ ├── ViewEnginesTest.cs │ │ │ ├── ViewMasterPageControlBuilderTest.cs │ │ │ ├── ViewMasterPageTest.cs │ │ │ ├── ViewPageControlBuilderTest.cs │ │ │ ├── ViewPageTest.cs │ │ │ ├── ViewResultBaseTest.cs │ │ │ ├── ViewResultTest.cs │ │ │ ├── ViewStartPageTest.cs │ │ │ ├── ViewTypeParserFilterTest.cs │ │ │ ├── ViewUserControlControlBuilderTest.cs │ │ │ ├── ViewUserControlTest.cs │ │ │ ├── VirtualPathProviderViewEngineTest.cs │ │ │ ├── WebFormViewEngineTest.cs │ │ │ └── WebFormViewTest.cs │ │ ├── Util/ │ │ │ ├── AnonymousObject.cs │ │ │ ├── DictionaryHelper.cs │ │ │ ├── HttpContextHelpers.cs │ │ │ ├── MvcHelper.cs │ │ │ ├── Resolver.cs │ │ │ ├── SimpleValueProvider.cs │ │ │ └── SimpleViewDataContainer.cs │ │ └── packages.config │ ├── System.Web.Razor.Test/ │ │ ├── CSharpRazorCodeLanguageTest.cs │ │ ├── CodeCompileUnitExtensions.cs │ │ ├── Editor/ │ │ │ └── RazorEditorParserTest.cs │ │ ├── Framework/ │ │ │ ├── BlockExtensions.cs │ │ │ ├── BlockTypes.cs │ │ │ ├── CodeParserTestBase.cs │ │ │ ├── CsHtmlCodeParserTestBase.cs │ │ │ ├── CsHtmlMarkupParserTestBase.cs │ │ │ ├── ErrorCollector.cs │ │ │ ├── MarkupParserTestBase.cs │ │ │ ├── ParserTestBase.cs │ │ │ ├── RawTextSymbol.cs │ │ │ ├── TestSpanBuilder.cs │ │ │ ├── VBHtmlCodeParserTestBase.cs │ │ │ └── VBHtmlMarkupParserTestBase.cs │ │ ├── Generator/ │ │ │ ├── CSharpRazorCodeGeneratorTest.cs │ │ │ ├── GeneratedCodeMappingTest.cs │ │ │ ├── PaddingTest.cs │ │ │ ├── RazorCodeGeneratorTest.cs │ │ │ ├── TabTest.cs │ │ │ ├── TestSpan.cs │ │ │ └── VBRazorCodeGeneratorTest.cs │ │ ├── Parser/ │ │ │ ├── BlockTest.cs │ │ │ ├── CSharp/ │ │ │ │ ├── CSharpAutoCompleteTest.cs │ │ │ │ ├── CSharpBlockTest.cs │ │ │ │ ├── CSharpDirectivesTest.cs │ │ │ │ ├── CSharpErrorTest.cs │ │ │ │ ├── CSharpExplicitExpressionTest.cs │ │ │ │ ├── CSharpHelperTest.cs │ │ │ │ ├── CSharpImplicitExpressionTest.cs │ │ │ │ ├── CSharpLayoutDirectiveTest.cs │ │ │ │ ├── CSharpNestedStatementsTest.cs │ │ │ │ ├── CSharpRazorCommentsTest.cs │ │ │ │ ├── CSharpReservedWordsTest.cs │ │ │ │ ├── CSharpSectionTest.cs │ │ │ │ ├── CSharpSpecialBlockTest.cs │ │ │ │ ├── CSharpStatementTest.cs │ │ │ │ ├── CSharpTemplateTest.cs │ │ │ │ ├── CSharpToMarkupSwitchTest.cs │ │ │ │ ├── CSharpVerbatimBlockTest.cs │ │ │ │ ├── CSharpWhitespaceHandlingTest.cs │ │ │ │ └── CsHtmlDocumentTest.cs │ │ │ ├── CallbackParserListenerTest.cs │ │ │ ├── Html/ │ │ │ │ ├── HtmlAttributeTest.cs │ │ │ │ ├── HtmlBlockTest.cs │ │ │ │ ├── HtmlDocumentTest.cs │ │ │ │ ├── HtmlErrorTest.cs │ │ │ │ ├── HtmlParserTestUtils.cs │ │ │ │ ├── HtmlTagsTest.cs │ │ │ │ ├── HtmlToCodeSwitchTest.cs │ │ │ │ └── HtmlUrlAttributeTest.cs │ │ │ ├── ParserContextTest.cs │ │ │ ├── ParserVisitorExtensionsTest.cs │ │ │ ├── PartialParsing/ │ │ │ │ ├── CSharpPartialParsingTest.cs │ │ │ │ ├── PartialParsingTestBase.cs │ │ │ │ └── VBPartialParsingTest.cs │ │ │ ├── RazorParserTest.cs │ │ │ ├── VB/ │ │ │ │ ├── VBAutoCompleteTest.cs │ │ │ │ ├── VBBlockTest.cs │ │ │ │ ├── VBContinueStatementTest.cs │ │ │ │ ├── VBDirectiveTest.cs │ │ │ │ ├── VBErrorTest.cs │ │ │ │ ├── VBExitStatementTest.cs │ │ │ │ ├── VBExplicitExpressionTest.cs │ │ │ │ ├── VBExpressionTest.cs │ │ │ │ ├── VBExpressionsInCodeTest.cs │ │ │ │ ├── VBHelperTest.cs │ │ │ │ ├── VBHtmlDocumentTest.cs │ │ │ │ ├── VBImplicitExpressionTest.cs │ │ │ │ ├── VBLayoutDirectiveTest.cs │ │ │ │ ├── VBNestedStatementsTest.cs │ │ │ │ ├── VBRazorCommentsTest.cs │ │ │ │ ├── VBReservedWordsTest.cs │ │ │ │ ├── VBSectionTest.cs │ │ │ │ ├── VBSpecialKeywordsTest.cs │ │ │ │ ├── VBStatementTest.cs │ │ │ │ ├── VBTemplateTest.cs │ │ │ │ └── VBToMarkupSwitchTest.cs │ │ │ └── WhitespaceRewriterTest.cs │ │ ├── RazorCodeLanguageTest.cs │ │ ├── RazorDirectiveAttributeTest.cs │ │ ├── RazorEngineHostTest.cs │ │ ├── RazorTemplateEngineTest.cs │ │ ├── StringTextBuffer.cs │ │ ├── System.Web.Razor.Test.csproj │ │ ├── TestFiles/ │ │ │ ├── CodeGenerator/ │ │ │ │ ├── CS/ │ │ │ │ │ ├── Output/ │ │ │ │ │ │ ├── Blocks.cs │ │ │ │ │ │ ├── CodeBlock.cs │ │ │ │ │ │ ├── CodeBlockAtEOF.cs │ │ │ │ │ │ ├── Comments.cs │ │ │ │ │ │ ├── ConditionalAttributes.cs │ │ │ │ │ │ ├── DesignTime.Tabs.cs │ │ │ │ │ │ ├── DesignTime.cs │ │ │ │ │ │ ├── EmptyCodeBlock.cs │ │ │ │ │ │ ├── EmptyExplicitExpression.cs │ │ │ │ │ │ ├── EmptyImplicitExpression.cs │ │ │ │ │ │ ├── EmptyImplicitExpressionInCode.Tabs.cs │ │ │ │ │ │ ├── EmptyImplicitExpressionInCode.cs │ │ │ │ │ │ ├── ExplicitExpression.cs │ │ │ │ │ │ ├── ExplicitExpressionAtEOF.cs │ │ │ │ │ │ ├── ExpressionsInCode.cs │ │ │ │ │ │ ├── FunctionsBlock.DesignTime.Tabs.cs │ │ │ │ │ │ ├── FunctionsBlock.DesignTime.cs │ │ │ │ │ │ ├── FunctionsBlock.cs │ │ │ │ │ │ ├── FunctionsBlockMinimal.DesignTime.Tabs.cs │ │ │ │ │ │ ├── FunctionsBlock_Tabs.cs │ │ │ │ │ │ ├── Helpers.Instance.cs │ │ │ │ │ │ ├── Helpers.cs │ │ │ │ │ │ ├── HelpersMissingCloseParen.cs │ │ │ │ │ │ ├── HelpersMissingName.cs │ │ │ │ │ │ ├── HelpersMissingOpenBrace.cs │ │ │ │ │ │ ├── HelpersMissingOpenParen.cs │ │ │ │ │ │ ├── HiddenSpansInCode.cs │ │ │ │ │ │ ├── HtmlCommentWithQuote_Double.cs │ │ │ │ │ │ ├── HtmlCommentWithQuote_Single.cs │ │ │ │ │ │ ├── ImplicitExpression.cs │ │ │ │ │ │ ├── ImplicitExpressionAtEOF.cs │ │ │ │ │ │ ├── Imports.DesignTime.cs │ │ │ │ │ │ ├── Imports.cs │ │ │ │ │ │ ├── Inherits.Designtime.cs │ │ │ │ │ │ ├── Inherits.Runtime.cs │ │ │ │ │ │ ├── InlineBlocks.cs │ │ │ │ │ │ ├── Instrumented.cs │ │ │ │ │ │ ├── LayoutDirective.cs │ │ │ │ │ │ ├── MarkupInCodeBlock.cs │ │ │ │ │ │ ├── NestedCodeBlocks.cs │ │ │ │ │ │ ├── NestedHelpers.cs │ │ │ │ │ │ ├── NoLinePragmas.cs │ │ │ │ │ │ ├── OpenedIf.DesignTime.Tabs.cs │ │ │ │ │ │ ├── OpenedIf.DesignTime.cs │ │ │ │ │ │ ├── ParserError.cs │ │ │ │ │ │ ├── RazorComments.DesignTime.cs │ │ │ │ │ │ ├── RazorComments.cs │ │ │ │ │ │ ├── ResolveUrl.cs │ │ │ │ │ │ ├── Sections.cs │ │ │ │ │ │ ├── SimpleUnspacedIf.DesignTime.Tabs.cs │ │ │ │ │ │ ├── Templates.cs │ │ │ │ │ │ ├── UnfinishedExpressionInCode.Tabs.cs │ │ │ │ │ │ └── UnfinishedExpressionInCode.cs │ │ │ │ │ └── Source/ │ │ │ │ │ ├── Blocks.cshtml │ │ │ │ │ ├── CodeBlock.cshtml │ │ │ │ │ ├── CodeBlockAtEOF.cshtml │ │ │ │ │ ├── ConditionalAttributes.cshtml │ │ │ │ │ ├── DesignTime.cshtml │ │ │ │ │ ├── EmptyCodeBlock.cshtml │ │ │ │ │ ├── EmptyExplicitExpression.cshtml │ │ │ │ │ ├── EmptyImplicitExpression.cshtml │ │ │ │ │ ├── EmptyImplicitExpressionInCode.cshtml │ │ │ │ │ ├── ExplicitExpression.cshtml │ │ │ │ │ ├── ExplicitExpressionAtEOF.cshtml │ │ │ │ │ ├── ExpressionsInCode.cshtml │ │ │ │ │ ├── FunctionsBlock.cshtml │ │ │ │ │ ├── FunctionsBlockMinimal.cshtml │ │ │ │ │ ├── FunctionsBlock_Tabs.cshtml │ │ │ │ │ ├── Helpers.cshtml │ │ │ │ │ ├── HelpersMissingCloseParen.cshtml │ │ │ │ │ ├── HelpersMissingName.cshtml │ │ │ │ │ ├── HelpersMissingOpenBrace.cshtml │ │ │ │ │ ├── HelpersMissingOpenParen.cshtml │ │ │ │ │ ├── HiddenSpansInCode.cshtml │ │ │ │ │ ├── HtmlCommentWithQuote_Double.cshtml │ │ │ │ │ ├── HtmlCommentWithQuote_Single.cshtml │ │ │ │ │ ├── ImplicitExpression.cshtml │ │ │ │ │ ├── ImplicitExpressionAtEOF.cshtml │ │ │ │ │ ├── Imports.cshtml │ │ │ │ │ ├── Inherits.cshtml │ │ │ │ │ ├── InlineBlocks.cshtml │ │ │ │ │ ├── Instrumented.cshtml │ │ │ │ │ ├── LayoutDirective.cshtml │ │ │ │ │ ├── MarkupInCodeBlock.cshtml │ │ │ │ │ ├── NestedCodeBlocks.cshtml │ │ │ │ │ ├── NestedHelpers.cshtml │ │ │ │ │ ├── NoLinePragmas.cshtml │ │ │ │ │ ├── OpenedIf.cshtml │ │ │ │ │ ├── ParserError.cshtml │ │ │ │ │ ├── RazorComments.cshtml │ │ │ │ │ ├── ResolveUrl.cshtml │ │ │ │ │ ├── Sections.cshtml │ │ │ │ │ ├── SimpleUnspacedIf.cshtml │ │ │ │ │ ├── Templates.cshtml │ │ │ │ │ └── UnfinishedExpressionInCode.cshtml │ │ │ │ └── VB/ │ │ │ │ ├── Output/ │ │ │ │ │ ├── Blocks.vb │ │ │ │ │ ├── CodeBlock.vb │ │ │ │ │ ├── CodeBlockAtEOF.vb │ │ │ │ │ ├── Comments.vb │ │ │ │ │ ├── ConditionalAttributes.vb │ │ │ │ │ ├── DesignTime.vb │ │ │ │ │ ├── EmptyExplicitExpression.vb │ │ │ │ │ ├── EmptyImplicitExpression.vb │ │ │ │ │ ├── EmptyImplicitExpressionInCode.vb │ │ │ │ │ ├── EmptySection.vb │ │ │ │ │ ├── ExplicitExpression.vb │ │ │ │ │ ├── ExplicitExpressionAtEOF.vb │ │ │ │ │ ├── ExpressionsInCode.vb │ │ │ │ │ ├── FunctionsBlock.DesignTime.Tabs.vb │ │ │ │ │ ├── FunctionsBlock.DesignTime.vb │ │ │ │ │ ├── FunctionsBlock.vb │ │ │ │ │ ├── Helpers.Instance.vb │ │ │ │ │ ├── Helpers.vb │ │ │ │ │ ├── HelpersMissingCloseParen.vb │ │ │ │ │ ├── HelpersMissingName.vb │ │ │ │ │ ├── HelpersMissingOpenParen.vb │ │ │ │ │ ├── ImplicitExpression.vb │ │ │ │ │ ├── ImplicitExpressionAtEOF.vb │ │ │ │ │ ├── Imports.DesignTime.vb │ │ │ │ │ ├── Imports.vb │ │ │ │ │ ├── Inherits.Designtime.vb │ │ │ │ │ ├── Inherits.Runtime.vb │ │ │ │ │ ├── Instrumented.vb │ │ │ │ │ ├── LayoutDirective.vb │ │ │ │ │ ├── MarkupInCodeBlock.vb │ │ │ │ │ ├── NestedCodeBlocks.vb │ │ │ │ │ ├── NestedHelpers.vb │ │ │ │ │ ├── NoLinePragmas.vb │ │ │ │ │ ├── Options.vb │ │ │ │ │ ├── ParserError.vb │ │ │ │ │ ├── RazorComments.DesignTime.vb │ │ │ │ │ ├── RazorComments.vb │ │ │ │ │ ├── ResolveUrl.vb │ │ │ │ │ ├── Sections.vb │ │ │ │ │ ├── Templates.vb │ │ │ │ │ └── UnfinishedExpressionInCode.vb │ │ │ │ └── Source/ │ │ │ │ ├── Blocks.vbhtml │ │ │ │ ├── CodeBlock.vbhtml │ │ │ │ ├── CodeBlockAtEOF.vbhtml │ │ │ │ ├── ConditionalAttributes.vbhtml │ │ │ │ ├── DesignTime.vbhtml │ │ │ │ ├── EmptyExplicitExpression.vbhtml │ │ │ │ ├── EmptyImplicitExpression.vbhtml │ │ │ │ ├── EmptyImplicitExpressionInCode.vbhtml │ │ │ │ ├── EmptySection.vbhtml │ │ │ │ ├── ExplicitExpression.vbhtml │ │ │ │ ├── ExplicitExpressionAtEOF.vbhtml │ │ │ │ ├── ExpressionsInCode.vbhtml │ │ │ │ ├── FunctionsBlock.vbhtml │ │ │ │ ├── Helpers.vbhtml │ │ │ │ ├── HelpersMissingCloseParen.vbhtml │ │ │ │ ├── HelpersMissingName.vbhtml │ │ │ │ ├── HelpersMissingOpenParen.vbhtml │ │ │ │ ├── ImplicitExpression.vbhtml │ │ │ │ ├── ImplicitExpressionAtEOF.vbhtml │ │ │ │ ├── Imports.vbhtml │ │ │ │ ├── Inherits.vbhtml │ │ │ │ ├── Instrumented.vbhtml │ │ │ │ ├── LayoutDirective.vbhtml │ │ │ │ ├── MarkupInCodeBlock.vbhtml │ │ │ │ ├── NestedCodeBlocks.vbhtml │ │ │ │ ├── NestedHelpers.vbhtml │ │ │ │ ├── NoLinePragmas.vbhtml │ │ │ │ ├── Options.vbhtml │ │ │ │ ├── ParserError.vbhtml │ │ │ │ ├── RazorComments.vbhtml │ │ │ │ ├── ResolveUrl.vbhtml │ │ │ │ ├── Sections.vbhtml │ │ │ │ ├── Templates.vbhtml │ │ │ │ └── UnfinishedExpressionInCode.vbhtml │ │ │ ├── DesignTime/ │ │ │ │ ├── Simple.cshtml │ │ │ │ └── Simple.txt │ │ │ └── nested-1000.html │ │ ├── Text/ │ │ │ ├── BufferingTextReaderTest.cs │ │ │ ├── LineTrackingStringBufferTest.cs │ │ │ ├── LookaheadTextReaderTestBase.cs │ │ │ ├── SourceLocationTest.cs │ │ │ ├── SourceLocationTrackerTest.cs │ │ │ ├── TextBufferReaderTest.cs │ │ │ ├── TextChangeTest.cs │ │ │ └── TextReaderExtensionsTest.cs │ │ ├── Tokenizer/ │ │ │ ├── CSharpTokenizerCommentTest.cs │ │ │ ├── CSharpTokenizerIdentifierTest.cs │ │ │ ├── CSharpTokenizerLiteralTest.cs │ │ │ ├── CSharpTokenizerOperatorsTest.cs │ │ │ ├── CSharpTokenizerTest.cs │ │ │ ├── CSharpTokenizerTestBase.cs │ │ │ ├── HtmlTokenizerTest.cs │ │ │ ├── HtmlTokenizerTestBase.cs │ │ │ ├── TokenizerLookaheadTest.cs │ │ │ ├── TokenizerTestBase.cs │ │ │ ├── VBTokenizerCommentTest.cs │ │ │ ├── VBTokenizerIdentifierTest.cs │ │ │ ├── VBTokenizerLiteralTest.cs │ │ │ ├── VBTokenizerOperatorsTest.cs │ │ │ ├── VBTokenizerTest.cs │ │ │ └── VBTokenizerTestBase.cs │ │ ├── Utils/ │ │ │ ├── DisposableActionTest.cs │ │ │ ├── EnumerableUtils.cs │ │ │ ├── MiscAssert.cs │ │ │ ├── MiscUtils.cs │ │ │ └── SpanAssert.cs │ │ ├── VBRazorCodeLanguageTest.cs │ │ └── packages.config │ ├── System.Web.WebPages.Administration.Test/ │ │ ├── AdminPackageTest.cs │ │ ├── PackageManagerModuleTest.cs │ │ ├── PackagesSourceFileTest.cs │ │ ├── PageUtilsTest.cs │ │ ├── PreApplicationStartCodeTest.cs │ │ ├── RemoteAssemblyTest.cs │ │ ├── System.Web.WebPages.Administration.Test.csproj │ │ ├── WebProjectManagerTest.cs │ │ ├── WebProjectSystemTest.cs │ │ └── packages.config │ ├── System.Web.WebPages.Deployment.Test/ │ │ ├── App.Config │ │ ├── AssemblyUtilsTest.cs │ │ ├── DeploymentUtil.cs │ │ ├── LatestRazorVersion.cs │ │ ├── PreApplicationStartCodeTest.cs │ │ ├── System.Web.WebPages.Deployment.Test.csproj │ │ ├── TestFileSystem.cs │ │ ├── TestFiles/ │ │ │ └── ConfigTestSites/ │ │ │ ├── CshtmlFileConfigV1/ │ │ │ │ ├── Default.cshtml │ │ │ │ └── web.config │ │ │ ├── CshtmlFileNoVersion/ │ │ │ │ └── Default.cshtml │ │ │ ├── NoCshtml/ │ │ │ │ └── Default.htm │ │ │ ├── NoCshtmlConfigv1/ │ │ │ │ ├── Default.htm │ │ │ │ └── web.config │ │ │ ├── NoCshtmlNoConfigSetting/ │ │ │ │ ├── Default.htm │ │ │ │ └── web.config │ │ │ ├── NoCshtmlWithEnabledSetting/ │ │ │ │ ├── Default.htm │ │ │ │ └── web.config │ │ │ └── NoCshtmlWithEnabledSettingFalse/ │ │ │ ├── Default.htm │ │ │ └── web.config │ │ ├── WebPagesDeploymentTest.cs │ │ └── packages.config │ ├── System.Web.WebPages.Razor.Test/ │ │ ├── PreApplicationStartCodeTest.cs │ │ ├── RazorBuildProviderTest.cs │ │ ├── System.Web.WebPages.Razor.Test.csproj │ │ ├── WebCodeRazorEngineHostTest.cs │ │ ├── WebPageRazorEngineHostTest.cs │ │ ├── WebRazorHostFactoryTest.cs │ │ ├── app.config │ │ └── packages.config │ ├── System.Web.WebPages.Test/ │ │ ├── App.config │ │ ├── ApplicationParts/ │ │ │ ├── ApplicationPartRegistryTest.cs │ │ │ ├── ApplicationPartTest.cs │ │ │ ├── MimeMappingTest.cs │ │ │ ├── ResourceHandlerTest.cs │ │ │ └── TestResourceAssembly.cs │ │ ├── Extensions/ │ │ │ ├── HttpContextExtensionsTest.cs │ │ │ ├── HttpRequestExtensionsTest.cs │ │ │ ├── HttpResponseExtensionsTest.cs │ │ │ ├── StringExtensionsTest.cs │ │ │ └── StringWriterExtensionsTest.cs │ │ ├── Helpers/ │ │ │ ├── AntiForgeryConfigTest.cs │ │ │ ├── AntiForgeryTest.cs │ │ │ ├── AntiXsrf/ │ │ │ │ ├── AntiForgeryTokenSerializerTest.cs │ │ │ │ ├── AntiForgeryTokenStoreTest.cs │ │ │ │ ├── AntiForgeryTokenTest.cs │ │ │ │ ├── AntiForgeryWorkerTest.cs │ │ │ │ ├── BinaryBlobTest.cs │ │ │ │ ├── ClaimUidExtractorTest.cs │ │ │ │ ├── HexUtil.cs │ │ │ │ ├── MachineKey45CryptoSystemTest.cs │ │ │ │ ├── MockAntiForgeryConfig.cs │ │ │ │ ├── MockableAntiForgeryTokenSerializer.cs │ │ │ │ ├── MockableClaimUidExtractor.cs │ │ │ │ ├── MockableCryptoSystem.cs │ │ │ │ ├── MockableTokenStore.cs │ │ │ │ ├── MockableTokenValidator.cs │ │ │ │ └── TokenValidatorTest.cs │ │ │ ├── Claims/ │ │ │ │ ├── ClaimTest.cs │ │ │ │ ├── ClaimsIdentityConverterTest.cs │ │ │ │ ├── ClaimsIdentityTest.cs │ │ │ │ └── MockClaimsIdentity.cs │ │ │ ├── CryptoUtilTest.cs │ │ │ └── UnvalidatedRequestValuesTest.cs │ │ ├── Html/ │ │ │ ├── CheckBoxTest.cs │ │ │ ├── HtmlHelperFactory.cs │ │ │ ├── HtmlHelperTest.cs │ │ │ ├── InputHelperTest.cs │ │ │ ├── LabelHelperTest.cs │ │ │ ├── RadioButtonTest.cs │ │ │ ├── SelectHelperTest.cs │ │ │ ├── TextAreaHelperTest.cs │ │ │ └── ValidationHelperTest.cs │ │ ├── Instrumentation/ │ │ │ └── InstrumentationServiceTest.cs │ │ ├── LatestRazorVersion.cs │ │ ├── Mvc/ │ │ │ ├── HttpAntiForgeryExceptionTest.cs │ │ │ ├── ModelClientValidationMaxLengthRuleTest.cs │ │ │ ├── ModelClientValidationMembershipPasswordRuleTest.cs │ │ │ ├── ModelClientValidationMinLengthRuleTest.cs │ │ │ ├── ModelClientValidationStringLengthRuleTest.cs │ │ │ ├── TagBuilderTest.cs │ │ │ └── UnobtrusiveValidationAttributesGeneratorTest.cs │ │ ├── PreApplicationStartCodeTest.cs │ │ ├── ScopeStorage/ │ │ │ ├── AspNetRequestScopeStorageProviderTest.cs │ │ │ ├── ScopeStorageDictionaryTest.cs │ │ │ ├── ScopeStorageKeyComparerTest.cs │ │ │ └── WebConfigScopeStorageTest.cs │ │ ├── System.Web.WebPages.Test.csproj │ │ ├── TestFiles/ │ │ │ └── Deployed/ │ │ │ ├── Bar │ │ │ ├── Bar.cshtml │ │ │ └── Bar.foohtml │ │ ├── Utils/ │ │ │ ├── CultureUtilTest.cs │ │ │ ├── HtmlAttributePropertyHelperTest.cs │ │ │ ├── PathUtilTest.cs │ │ │ ├── PropertyHelperTest.cs │ │ │ ├── SessionStateUtilTest.cs │ │ │ ├── TestObjectFactory.cs │ │ │ ├── TypeHelperTest.cs │ │ │ ├── UrlRewriterHelperTest.cs │ │ │ └── UrlUtilTest.cs │ │ ├── Validation/ │ │ │ ├── ValidationHelperTest.cs │ │ │ └── ValidatorTest.cs │ │ ├── WebPage/ │ │ │ ├── ApplicationStartPageTest.cs │ │ │ ├── BrowserHelpersTest.cs │ │ │ ├── BrowserOverrideStoresTest.cs │ │ │ ├── BuildManagerExceptionUtilTest.cs │ │ │ ├── BuildManagerWrapperTest.cs │ │ │ ├── CookieBrowserOverrideStoreTest.cs │ │ │ ├── DefaultDisplayModeTest.cs │ │ │ ├── DisplayInfoTest.cs │ │ │ ├── DisplayModeProviderTest.cs │ │ │ ├── DynamicHttpApplicationStateTest.cs │ │ │ ├── DynamicPageDataDictionaryTest.cs │ │ │ ├── FileExistenceCacheTest.cs │ │ │ ├── LayoutTest.cs │ │ │ ├── PageDataDictionaryTest.cs │ │ │ ├── RenderPageTest.cs │ │ │ ├── RequestBrowserOverrideStoreTest.cs │ │ │ ├── RequestResourceTrackerTest.cs │ │ │ ├── StartPageTest.cs │ │ │ ├── TemplateStackTest.cs │ │ │ ├── UrlDataTest.cs │ │ │ ├── Utils.cs │ │ │ ├── VirtualPathFactoryExtensionsTest.cs │ │ │ ├── VirtualPathFactoryManagerTest.cs │ │ │ ├── WebPageContextTest.cs │ │ │ ├── WebPageExecutingBaseTest.cs │ │ │ ├── WebPageHttpHandlerTest.cs │ │ │ ├── WebPageHttpModuleTest.cs │ │ │ ├── WebPageRenderingBaseTest.cs │ │ │ ├── WebPageRouteTest.cs │ │ │ └── WebPageTest.cs │ │ └── packages.config │ ├── WebApiHelpPage.Test/ │ │ ├── Controllers/ │ │ │ ├── UsersController.cs │ │ │ └── ValuesController.cs │ │ ├── HelpControllerTest.cs │ │ ├── HelpPageApiModelTest.cs │ │ ├── HelpPageAreaRegistrationTest.cs │ │ ├── HelpPageConfigurationExtensionsTest.cs │ │ ├── HelpPageSampleKeyTest.cs │ │ ├── Helpers/ │ │ │ ├── ApiDescriptionHelpers.cs │ │ │ └── CustomTypes.cs │ │ ├── HelppageSampleGeneratorTest.cs │ │ ├── ImageSampleTest.cs │ │ ├── InvalidSampleTest.cs │ │ ├── ModelDescriptionGeneratorTest.cs │ │ ├── ObjectGeneratorTest.cs │ │ ├── TextSampleTest.cs │ │ ├── WebApiHelpPage.Test.csproj │ │ ├── WebConfigTest.cs │ │ ├── XmlDocumentationProviderTest.cs │ │ └── packages.config │ ├── WebApiHelpPage.VB.Test/ │ │ └── WebApiHelpPage.VB.Test.csproj │ ├── WebMatrix.Data.Test/ │ │ ├── App.config │ │ ├── ConfigurationManagerWrapperTest.cs │ │ ├── DatabaseTest.cs │ │ ├── DynamicRecordTest.cs │ │ ├── FileHandlerTest.cs │ │ ├── Mocks/ │ │ │ ├── MockConfigurationManager.cs │ │ │ ├── MockConnectionConfiguration.cs │ │ │ ├── MockDbFileHandler.cs │ │ │ └── MockDbProviderFactory.cs │ │ ├── WebMatrix.Data.Test.csproj │ │ └── packages.config │ └── WebMatrix.WebData.Test/ │ ├── MockDatabase.cs │ ├── PreApplicationStartCodeTest.cs │ ├── SimpleMembershipProviderTest.cs │ ├── SimpleRoleProviderTest.cs │ ├── WebMatrix.WebData.Test.csproj │ ├── WebSecurityTest.cs │ └── packages.config └── tools/ ├── 35MSSharedLib1024.snk ├── SkipStrongNames.xml ├── WebStack.StyleCop.targets ├── WebStack.settings.targets ├── WebStack.targets ├── WebStack.tasks.targets ├── WebStack.testing.targets ├── src/ │ └── Microsoft.Web.FxCop/ │ ├── DoNotCallProblematicMethodsOnTaskRule.cs │ ├── DoNotConstructTaskInstancesRule.cs │ ├── DoNotUseFinalizersRule.cs │ ├── DoNotUseProblematicTaskTypesRule.cs │ ├── IntrospectionRule.cs │ ├── Microsoft.Web.FxCop.csproj │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Rules.xml │ ├── TypeNodeExtensions.cs │ └── UnusedResourceUsageRule.cs └── vslicense/ ├── dotnet-library-license.htm └── dotnet-library-license.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .appveyor.yml ================================================ init: - git config --global core.autocrlf true branches: only: - main - /^(.*\/)?ci-.*$/ - /^rel\/.*/ configuration: - CodeAnalysis - Release matrix: fast_finish: true before_build: - cmd: .\build.cmd EnableSkipStrongNames build_script: - cmd: .\build.cmd UnitTest /P:Configuration=%Configuration% clone_depth: 1 test: off deploy: off os: Visual Studio 2017 ================================================ FILE: .azuredevops/dependabot.yml ================================================ version: 2 enable-campaigned-updates: false enable-security-updates: false ================================================ FILE: .config/CredScanSuppressions.json ================================================ { "tool": "Credential Scanner", "suppressions": [ { "placeholder": "abcdefg", "_justification": "This is a fake password used in test code." }, { "placeholder": "Pa$$AAECAw==", "_justification": "This is a fake password used in test code." }, { "placeholder": "ALyuoraY/cIWD1hjo+K81/pf83qo6Q6T+UBYcXN9P3A9WHLvEY10f+lwW5qPG6h9xw==", "_justification": "This is a fake hashed password used in test code." }, { "placeholder": "abcdefg123", "_justification": "This is a fake password used in test code." }, { "placeholder": "3e29b24f825e737d97aed5eb62df5076", "_justification": "This is a fake password used in test code." }, { "placeholder": "My Password", "_justification": "This is a fake password used in test code." } ] } ================================================ FILE: .config/tsaoptions.json ================================================ { "areaPath": "DevDiv\\ASP.NET Core\\Policy Violations", "codebaseName": "AspNetWebStack", "instanceUrl": "https://devdiv.visualstudio.com/", "iterationPath": "DevDiv", "notificationAliases": [ "aspnetcore-build@microsoft.com" ], "projectName": "DEVDIV", "repositoryName": "AspNetWebStack", "template": "TFSDEVDIV" } ================================================ FILE: .gitattributes ================================================ * text *.bmp binary *.dll binary *.gif binary *.jpg binary *.png binary *.snk binary *.ascx text *.cd text *.cmd text *.coffee text *.config text *.cs text diff=csharp *.csproj text merge=union *.cshtml text *.css text *.dtd text *.edmx text *.htm text *.html text *.js text *.json text *.msbuild text *.nuspec text *.resx text *.ruleset text *.StyleCop text *.targets text *.tt text *.txt text *.vb text *.vbhtml text *.vbproj text merge=union *.vbs text *.wsf text *.xml text *.xunit text *.sln text eol=crlf merge=union ================================================ FILE: .gitignore ================================================ .msbuild/ .vs/ bin/ obj/ packages/ *.[Cc]ache *.binlog *.dll *.dot[Cc]over *.exe *.nupkg *.orig *.psess *.sln.ide *.suo *.user *.vsp *[Rr]esharper* *launchSettings.json ================================================ FILE: .nuget/packages.config ================================================  ================================================ FILE: .travis.yml ================================================ language: csharp sudo: false dist: trusty mono: none os: - linux branches: only: - not.a.branch script: - echo Skipping builds for now. ================================================ FILE: CODE-OF-CONDUCT.md ================================================ # Code of Conduct This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). ================================================ FILE: CONTRIBUTING.md ================================================ # How to contribute One of the easiest ways to contribute is to participate in discussions and discuss issues. You can also contribute by submitting pull requests with code changes. ## Bugs and feature requests? For non-security related bugs please log a new issue in this repo. ## Reporting security issues and bugs Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx). ## Other discussions Our team members also monitor several other discussion forums: * [ASP.NET MVC forum](https://forums.asp.net/1146.aspx/1?MVC) * [ASP.NET Web API forum](https://forums.asp.net/1246.aspx/1?Web+API) * [ASP.NET Web Pages forum](https://forums.asp.net/1224.aspx/1?ASP+NET+Web+Pages) * [StackOverflow](https://stackoverflow.com/) with the [`asp.net`](https://stackoverflow.com/questions/tagged/asp.net), [`asp.net-mvc`](https://stackoverflow.com/questions/tagged/asp.net-mvc), [`asp.net-web-api`](https://stackoverflow.com/questions/tagged/asp.net-web-api), [`asp.net-webpages`](https://stackoverflow.com/questions/tagged/asp.net-webpages) or [`razor`](https://stackoverflow.com/questions/tagged/razor) tags. ## Filing issues When filing issues, please use our [bug filing templates](https://github.com/aspnet/Home/wiki/Functional-bug-template). The best way to get your bug fixed is to be as detailed as you can be about the problem. Providing a minimal project with steps to reproduce the problem is ideal. Here are questions you can answer before you file a bug to make sure you're not missing any important information. 1. Did you read the [documentation](http://www.asp.net/aspnet)? 2. Did you include the snippet of broken code in the issue? 3. What are the *EXACT* steps to reproduce this problem? 4. What package versions are you using (you can see these in the `packages.config` file)? 5. What operating system are you using? 6. What version of IIS are you using? GitHub supports [markdown](https://help.github.com/articles/github-flavored-markdown/), so when filing bugs make sure you check the formatting before clicking submit. ## Contributing code and content You will need to sign a [Contributor License Agreement](https://cla2.dotnetfoundation.org/) before submitting your pull request. To complete the Contributor License Agreement (CLA), you will need to submit a request via the form and then electronically sign the Contributor License Agreement when you receive the email containing the link to the document. This needs to only be done once for any .NET Foundation OSS project. Make sure you can build the code. Familiarize yourself with the project workflow and our coding conventions. If you don't know what a pull request is read this article: https://help.github.com/articles/using-pull-requests. Before submitting a feature or substantial code contribution please discuss it with the team and ensure it follows the product roadmap. You might also read these two blogs posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. Note that all code submissions will be rigorously reviewed and tested by the ASP.NET and Entity Framework teams, and only those that meet an extremely high bar for both quality and design/roadmap appropriateness will be merged into the source. Here's a few things you should always do when making changes to the code base: **Commit/Pull Request Format** ``` Summary of the changes (Less than 80 chars) - Detail 1 - Detail 2 Addresses #bugnumber (in this specific format) ``` **Tests** - Tests need to be provided for every bug/feature that is completed. - Tests only need to be present for issues that need to be verified by QA (e.g. not tasks) - If there is a scenario that is far too hard to test there does not need to be a test for it. - "Too hard" is determined by the team as a whole. ================================================ FILE: Directory.Build.props ================================================ 16.0 true false ================================================ FILE: Directory.Build.targets ================================================ ================================================ FILE: LICENSE.txt ================================================ Copyright (c) .NET Foundation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use these files except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: NuGet.Config ================================================  ================================================ FILE: README.md ================================================ # ASP.NET MVC, Web API, Web Pages, and Razor ## Note: This repo is for ASP.NET MVC 5.x, Web API 2.x, and Web Pages 3.x. For ASP.NET Core MVC, check the [AspNetCore repo](https://github.com/aspnet/AspNetCore). ASP.NET MVC is a web framework that gives you a powerful, patterns-based way to build dynamic websites and Web APIs. ASP.NET MVC enables a clean separation of concerns and gives you full control over markup. This repo includes: * ASP.NET MVC 5.x * ASP.NET Web API 2.x * ASP.NET Web Pages 3.x * ASP.NET Razor 3.x ### Contributing Check out the [contributing](CONTRIBUTING.md) page to see the best places to log issues and start discussions. ### Tags and releases Git tag or branch|Other products|MVC package versions|Web API package (product) versions|Web Pages package versions --------|--------------|------------|------------|------------ [v2.0.4](https://github.com/aspnet/AspNetWebStack/tree/v2.0.4)||4.0.40804|4.0.30506|2.0.30506 [v2.1](https://github.com/aspnet/AspNetWebStack/tree/v2.1)|ASP.NET and Web Tools 2012.2, VS 2012 Update 2 (not on http://nuget.org)|v4 2012.2 Update RTM|v1 2012.2 Update RTM|v2 2012.2 Update RTM [v3.0.2](https://github.com/aspnet/AspNetWebStack/tree/v3.0.2)||5.0.2|5.0.1 (2.0.1)|3.0.1 [v3.1.3](https://github.com/aspnet/AspNetWebStack/tree/v3.1.3)||5.1.3|5.1.2 (2.1.2)|3.1.2 [v3.2.6](https://github.com/aspnet/AspNetWebStack/tree/v3.2.6)||5.2.6|5.2.6|3.2.6 [v3.2.7](https://github.com/aspnet/AspNetWebStack/tree/v3.2.7)||5.2.7|5.2.7|3.2.7 [v3.2.8](https://github.com/aspnet/AspNetWebStack/tree/v3.2.8)||5.2.8|5.2.8|3.2.8 [main](https://github.com/aspnet/AspNetWebStack/tree/main)|New work e.g. MVC 5.2.9-preview1|||| ================================================ FILE: Runtime.NetFramework.slnf ================================================ { "solution": { "path": "Runtime.sln", "projects": [ "src\\Microsoft.AspNet.Facebook\\Microsoft.AspNet.Facebook.csproj", "src\\Microsoft.Web.Helpers\\Microsoft.Web.Helpers.csproj", "src\\Microsoft.Web.Mvc\\Microsoft.Web.Mvc.csproj", "src\\Microsoft.Web.WebPages.OAuth\\Microsoft.Web.WebPages.OAuth.csproj", "src\\System.Net.Http.Formatting\\System.Net.Http.Formatting.csproj", "src\\System.Web.Cors\\System.Web.Cors.csproj", "src\\System.Web.Helpers\\System.Web.Helpers.csproj", "src\\System.Web.Http.Cors\\System.Web.Http.Cors.csproj", "src\\System.Web.Http.Owin\\System.Web.Http.Owin.csproj", "src\\System.Web.Http.SelfHost\\System.Web.Http.SelfHost.csproj", "src\\System.Web.Http.SignalR\\System.Web.Http.SignalR.csproj", "src\\System.Web.Http.Tracing\\System.Web.Http.Tracing.csproj", "src\\System.Web.Http.WebHost\\System.Web.Http.WebHost.csproj", "src\\System.Web.Http\\System.Web.Http.csproj", "src\\System.Web.Mvc\\System.Web.Mvc.csproj", "src\\System.Web.Razor\\System.Web.Razor.csproj", "src\\System.Web.WebPages.Administration\\System.Web.WebPages.Administration.csproj", "src\\System.Web.WebPages.Deployment\\System.Web.WebPages.Deployment.csproj", "src\\System.Web.WebPages.Razor\\System.Web.WebPages.Razor.csproj", "src\\System.Web.WebPages\\System.Web.WebPages.csproj", "src\\WebApiHelpPage\\VB\\WebApiHelpPageVB.vbproj", "src\\WebApiHelpPage\\WebApiHelpPage.csproj", "src\\WebMatrix.Data\\WebMatrix.Data.csproj", "src\\WebMatrix.WebData\\WebMatrix.WebData.csproj", "test\\Microsoft.AspNet.Facebook.Test\\Microsoft.AspNet.Facebook.Test.csproj", "test\\Microsoft.TestCommon\\Microsoft.TestCommon.csproj", "test\\Microsoft.Web.Helpers.Test\\Microsoft.Web.Helpers.Test.csproj", "test\\Microsoft.Web.Mvc.Test\\Microsoft.Web.Mvc.Test.csproj", "test\\Microsoft.Web.WebPages.OAuth.Test\\Microsoft.Web.WebPages.OAuth.Test.csproj", "test\\System.Net.Http.Formatting.Test\\System.Net.Http.Formatting.Test.csproj", "test\\System.Web.Cors.Test\\System.Web.Cors.Test.csproj", "test\\System.Web.Helpers.Test\\System.Web.Helpers.Test.csproj", "test\\System.Web.Http.Cors.Test\\System.Web.Http.Cors.Test.csproj", "test\\System.Web.Http.Integration.Test\\System.Web.Http.Integration.Test.csproj", "test\\System.Web.Http.Owin.Test\\System.Web.Http.Owin.Test.csproj", "test\\System.Web.Http.SelfHost.Test\\System.Web.Http.SelfHost.Test.csproj", "test\\System.Web.Http.SignalR.Test\\System.Web.Http.SignalR.Test.csproj", "test\\System.Web.Http.Test\\System.Web.Http.Test.csproj", "test\\System.Web.Http.Tracing.Test\\System.Web.Http.Tracing.Test.csproj", "test\\System.Web.Http.WebHost.Test\\System.Web.Http.WebHost.Test.csproj", "test\\System.Web.Mvc.Test\\System.Web.Mvc.Test.csproj", "test\\System.Web.Razor.Test\\System.Web.Razor.Test.csproj", "test\\System.Web.WebPages.Administration.Test\\System.Web.WebPages.Administration.Test.csproj", "test\\System.Web.WebPages.Deployment.Test\\System.Web.WebPages.Deployment.Test.csproj", "test\\System.Web.WebPages.Razor.Test\\System.Web.WebPages.Razor.Test.csproj", "test\\System.Web.WebPages.Test\\System.Web.WebPages.Test.csproj", "test\\WebApiHelpPage.Test\\WebApiHelpPage.Test.csproj", "test\\WebApiHelpPage.VB.Test\\WebApiHelpPage.VB.Test.csproj", "test\\WebMatrix.Data.Test\\WebMatrix.Data.Test.csproj", "test\\WebMatrix.WebData.Test\\WebMatrix.WebData.Test.csproj" ] } } ================================================ FILE: Runtime.NetStandard.slnf ================================================ { "solution": { "path": "Runtime.sln", "projects": [ "src\\System.Net.Http.Formatting.ns1_3\\System.Net.Http.Formatting.ns1_3.csproj", "src\\System.Net.Http.Formatting.ns2_0\\System.Net.Http.Formatting.ns2_0.csproj", "test\\Microsoft.TestCommon\\Microsoft.TestCommon.csproj", "test\\System.Net.Http.Formatting.ns1_3.Test\\System.Net.Http.Formatting.ns1_3.Test.csproj", "test\\System.Net.Http.Formatting.ns2_0.Test\\System.Net.Http.Formatting.ns2_0.Test.csproj" ] } } ================================================ FILE: Runtime.msbuild ================================================ CodeAnalysis Release true true true false false false $(MSBuildThisFileDirectory)bin\$(Configuration)\test\TestResults\ $(MSBuildThisFileDirectory)packages\Microsoft.Web.SkipStrongNames.1.0.0\tools\SkipStrongNames.exe $(MSBuildThisFileDirectory)tools\SkipStrongNames.xml .nuget\NuGet.exe <_Testing_NetStandard1_3 Include="true;false" /> $(MSBuildThisFileDirectory)tools\src\Microsoft.Web.FxCop\ $(MSBuildThisFileDirectory)packages\CustomFxCopRules <_TestDLLsXunit Include="bin\$(Configuration)\test\*.Test.dll; bin\$(Configuration)\test\*\net4*\*.Test.dll" /> <_XunitProject Include="tools\WebStack.testing.targets"> TestAssembly=%(_TestDLLsXunit.FullPath); XmlPath=$(TestResultsDirectory)%(_TestDLLsXunit.FileName)-XunitResults.xml <_VSTestDLLs Include="bin\$(Configuration)\test\ns1_3\**\*.Test.dll; bin\$(Configuration)\test\ns2_0\**\*.Test.dll" Exclude="bin\$(Configuration)\test\*\net4*\*.Test.dll" /> <_XunitProject Include="tools\WebStack.testing.targets"> TestAssembly=%(_VSTestDLLs.FullPath); XmlPath=$(TestResultsDirectory)%(_VSTestDLLs.FileName)-$([System.String]::Copy('%(_VSTestDLLs.RecursiveDir)').Trim('\\'))-XunitResults.xml; UseVSTest=true ================================================ FILE: Runtime.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27016.1 MinimumVisualStudioVersion = 15.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C40883CD-366D-4534-8B58-3EA0D13136DF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Razor", "src\System.Web.Razor\System.Web.Razor.csproj", "{8F18041B-9410-4C36-A9C5-067813DF5F31}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Deployment", "src\System.Web.WebPages.Deployment\System.Web.WebPages.Deployment.csproj", "{22BABB60-8F02-4027-AFFC-ACF069954536}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages", "src\System.Web.WebPages\System.Web.WebPages.csproj", "{76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Helpers", "src\System.Web.Helpers\System.Web.Helpers.csproj", "{9B7E3740-6161-4548-833C-4BBCA43B970E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Razor", "src\System.Web.WebPages.Razor\System.Web.WebPages.Razor.csproj", "{0939B11A-FE4E-4BA1-8AD6-D97741EE314F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMatrix.Data", "src\WebMatrix.Data\WebMatrix.Data.csproj", "{4D39BAAF-8A96-473E-AB79-C8A341885137}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMatrix.WebData", "src\WebMatrix.WebData\WebMatrix.WebData.csproj", "{55A15F40-1435-4248-A7F2-2A146BB83586}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.Helpers", "src\Microsoft.Web.Helpers\Microsoft.Web.Helpers.csproj", "{0C7CE809-0F72-4C19-8C64-D6573E4D9521}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Administration", "src\System.Web.WebPages.Administration\System.Web.WebPages.Administration.csproj", "{C23F02FC-4538-43F5-ABBA-38BA069AEA8F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Mvc", "src\System.Web.Mvc\System.Web.Mvc.csproj", "{3D3FFD8A-624D-4E9B-954B-E1C105507975}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.Mvc", "src\Microsoft.Web.Mvc\Microsoft.Web.Mvc.csproj", "{D3CF7430-6DA4-42B0-BD90-CA39D16687B2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Razor.Test", "test\System.Web.Razor.Test\System.Web.Razor.Test.csproj", "{0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Deployment.Test", "test\System.Web.WebPages.Deployment.Test\System.Web.WebPages.Deployment.Test.csproj", "{268DEE9D-F323-4A00-8ED8-3784388C3E3A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Test", "test\System.Web.WebPages.Test\System.Web.WebPages.Test.csproj", "{0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Helpers.Test", "test\System.Web.Helpers.Test\System.Web.Helpers.Test.csproj", "{D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Razor.Test", "test\System.Web.WebPages.Razor.Test\System.Web.WebPages.Razor.Test.csproj", "{66A74F3C-A106-4C1E-BAA0-001908FEA2CA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMatrix.Data.Test", "test\WebMatrix.Data.Test\WebMatrix.Data.Test.csproj", "{E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebMatrix.WebData.Test", "test\WebMatrix.WebData.Test\WebMatrix.WebData.Test.csproj", "{CD48EB41-92A5-4628-A0F7-6A43DF58827E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.Helpers.Test", "test\Microsoft.Web.Helpers.Test\Microsoft.Web.Helpers.Test.csproj", "{2C653A66-8159-4A41-954F-A67915DFDA87}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.WebPages.Administration.Test", "test\System.Web.WebPages.Administration.Test\System.Web.WebPages.Administration.Test.csproj", "{21C729D6-ECF8-47EF-A236-7C6A4272EAF0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Mvc.Test", "test\System.Web.Mvc.Test\System.Web.Mvc.Test.csproj", "{8AC2A2E4-2F11-4D40-A887-62E2583A65E6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.Mvc.Test", "test\Microsoft.Web.Mvc.Test\Microsoft.Web.Mvc.Test.csproj", "{6C28DA70-60F1-4442-967F-591BF3962EC5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http", "src\System.Web.Http\System.Web.Http.csproj", "{DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Test", "test\System.Web.Http.Test\System.Web.Http.Test.csproj", "{7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Http.Formatting", "src\System.Net.Http.Formatting\System.Net.Http.Formatting.csproj", "{668E9021-CE84-49D9-98FB-DF125A9FCDB0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestCommon", "test\Microsoft.TestCommon\Microsoft.TestCommon.csproj", "{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Net.Http.Formatting.Test", "test\System.Net.Http.Formatting.Test\System.Net.Http.Formatting.Test.csproj", "{7AF77741-9158-4D5F-8782-8F21FADF025F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.SelfHost", "src\System.Web.Http.SelfHost\System.Web.Http.SelfHost.csproj", "{66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.WebHost", "src\System.Web.Http.WebHost\System.Web.Http.WebHost.csproj", "{A0187BC2-8325-4BB2-8697-7F955CF4173E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Integration.Test", "test\System.Web.Http.Integration.Test\System.Web.Http.Integration.Test.csproj", "{3267DFC6-B34D-4011-BC0F-D3B56AF6F608}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.WebHost.Test", "test\System.Web.Http.WebHost.Test\System.Web.Http.WebHost.Test.csproj", "{EA62944F-BD25-4730-9405-9BE8FF5BEACD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.WebPages.OAuth", "src\Microsoft.Web.WebPages.OAuth\Microsoft.Web.WebPages.OAuth.csproj", "{4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.WebPages.OAuth.Test", "test\Microsoft.Web.WebPages.OAuth.Test\Microsoft.Web.WebPages.OAuth.Test.csproj", "{694C6EDF-EA52-438F-B745-82B025ECC0E7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.SelfHost.Test", "test\System.Web.Http.SelfHost.Test\System.Web.Http.SelfHost.Test.csproj", "{7F29EE87-6A63-43C6-B7FF-74DD06815830}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiHelpPage", "src\WebApiHelpPage\WebApiHelpPage.csproj", "{FEDFE6CA-8282-4C5B-A756-E97189690982}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiHelpPage.Test", "test\WebApiHelpPage.Test\WebApiHelpPage.Test.csproj", "{291EF478-BF24-4935-BC78-E0DCCD0C9A1B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Tracing", "src\System.Web.Http.Tracing\System.Web.Http.Tracing.csproj", "{6E81EF98-8F5C-4EED-8B37-456991CA56FD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Tracing.Test", "test\System.Web.Http.Tracing.Test\System.Web.Http.Tracing.Test.csproj", "{F87FD911-4A97-4057-8EAE-1CB96B9A1937}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.SignalR", "src\System.Web.Http.SignalR\System.Web.Http.SignalR.csproj", "{8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.SignalR.Test", "test\System.Web.Http.SignalR.Test\System.Web.Http.SignalR.Test.csproj", "{E22245AF-D5E1-46F6-B443-C886983EC50C}" EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "WebApiHelpPageVB", "src\WebApiHelpPage\VB\WebApiHelpPageVB.vbproj", "{22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiHelpPage.VB.Test", "test\WebApiHelpPage.VB.Test\WebApiHelpPage.VB.Test.csproj", "{41D5691F-2720-44A0-9185-EEFE928D2679}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Owin", "src\System.Web.Http.Owin\System.Web.Http.Owin.csproj", "{66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Owin.Test", "test\System.Web.Http.Owin.Test\System.Web.Http.Owin.Test.csproj", "{C19267DD-3984-430C-AE18-4034F85DE4E5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{CB34D534-9A09-4EE4-B350-C1C23AFBF5EE}" ProjectSection(SolutionItems) = preProject global.json = global.json NuGet.Config = NuGet.Config .nuget\packages.config = .nuget\packages.config EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Cors", "src\System.Web.Cors\System.Web.Cors.csproj", "{43C1B979-D593-4A32-BB3A-4316F1C66D66}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Cors", "src\System.Web.Http.Cors\System.Web.Http.Cors.csproj", "{25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Cors.Test", "test\System.Web.Cors.Test\System.Web.Cors.Test.csproj", "{BF07E947-120D-4E93-93DA-A4BF121753EA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Web.Http.Cors.Test", "test\System.Web.Http.Cors.Test\System.Web.Http.Cors.Test.csproj", "{1E89A3E9-0A7F-418F-B4BE-6E38A6315373}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Facebook", "src\Microsoft.AspNet.Facebook\Microsoft.AspNet.Facebook.csproj", "{821A136C-7C6F-44C6-A9E6-C39B5BFB1483}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Facebook.Test", "test\Microsoft.AspNet.Facebook.Test\Microsoft.AspNet.Facebook.Test.csproj", "{C3BEF382-C7C4-454D-B017-1EAC03E9A82C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.ns1_3", "src\System.Net.Http.Formatting.ns1_3\System.Net.Http.Formatting.ns1_3.csproj", "{5ABD9968-F3A3-4967-B768-A6142F69759E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.ns2_0", "src\System.Net.Http.Formatting.ns2_0\System.Net.Http.Formatting.ns2_0.csproj", "{9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.ns1_3.Test", "test\System.Net.Http.Formatting.ns1_3.Test\System.Net.Http.Formatting.ns1_3.Test.csproj", "{A1A20049-04C2-4676-93CF-92449C4BBAA9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Net.Http.Formatting.ns2_0.Test", "test\System.Net.Http.Formatting.ns2_0.Test\System.Net.Http.Formatting.ns2_0.Test.csproj", "{6C320AD9-F380-4F8B-85F9-0689F88766EC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8F18041B-9410-4C36-A9C5-067813DF5F31}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {8F18041B-9410-4C36-A9C5-067813DF5F31}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {8F18041B-9410-4C36-A9C5-067813DF5F31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8F18041B-9410-4C36-A9C5-067813DF5F31}.Debug|Any CPU.Build.0 = Debug|Any CPU {8F18041B-9410-4C36-A9C5-067813DF5F31}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F18041B-9410-4C36-A9C5-067813DF5F31}.Release|Any CPU.Build.0 = Release|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.Debug|Any CPU.Build.0 = Debug|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.Release|Any CPU.ActiveCfg = Release|Any CPU {22BABB60-8F02-4027-AFFC-ACF069954536}.Release|Any CPU.Build.0 = Release|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.Debug|Any CPU.Build.0 = Debug|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2}.Release|Any CPU.Build.0 = Release|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B7E3740-6161-4548-833C-4BBCA43B970E}.Release|Any CPU.Build.0 = Release|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0939B11A-FE4E-4BA1-8AD6-D97741EE314F}.Release|Any CPU.Build.0 = Release|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.Debug|Any CPU.Build.0 = Debug|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D39BAAF-8A96-473E-AB79-C8A341885137}.Release|Any CPU.Build.0 = Release|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.Debug|Any CPU.Build.0 = Debug|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.Release|Any CPU.ActiveCfg = Release|Any CPU {55A15F40-1435-4248-A7F2-2A146BB83586}.Release|Any CPU.Build.0 = Release|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C7CE809-0F72-4C19-8C64-D6573E4D9521}.Release|Any CPU.Build.0 = Release|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.Debug|Any CPU.Build.0 = Debug|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {C23F02FC-4538-43F5-ABBA-38BA069AEA8F}.Release|Any CPU.Build.0 = Release|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D3FFD8A-624D-4E9B-954B-E1C105507975}.Release|Any CPU.Build.0 = Release|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3CF7430-6DA4-42B0-BD90-CA39D16687B2}.Release|Any CPU.Build.0 = Release|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Debug|Any CPU.Build.0 = Debug|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Release|Any CPU.ActiveCfg = Release|Any CPU {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Release|Any CPU.Build.0 = Release|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {268DEE9D-F323-4A00-8ED8-3784388C3E3A}.Release|Any CPU.Build.0 = Release|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2}.Release|Any CPU.Build.0 = Release|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57}.Release|Any CPU.Build.0 = Release|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {66A74F3C-A106-4C1E-BAA0-001908FEA2CA}.Release|Any CPU.Build.0 = Release|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A}.Release|Any CPU.Build.0 = Release|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD48EB41-92A5-4628-A0F7-6A43DF58827E}.Release|Any CPU.Build.0 = Release|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.Debug|Any CPU.Build.0 = Debug|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.Release|Any CPU.ActiveCfg = Release|Any CPU {2C653A66-8159-4A41-954F-A67915DFDA87}.Release|Any CPU.Build.0 = Release|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.Debug|Any CPU.Build.0 = Debug|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.Release|Any CPU.ActiveCfg = Release|Any CPU {21C729D6-ECF8-47EF-A236-7C6A4272EAF0}.Release|Any CPU.Build.0 = Release|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.Debug|Any CPU.Build.0 = Debug|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.Release|Any CPU.ActiveCfg = Release|Any CPU {8AC2A2E4-2F11-4D40-A887-62E2583A65E6}.Release|Any CPU.Build.0 = Release|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU {6C28DA70-60F1-4442-967F-591BF3962EC5}.Release|Any CPU.Build.0 = Release|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.Release|Any CPU.ActiveCfg = Release|Any CPU {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440}.Release|Any CPU.Build.0 = Release|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1}.Release|Any CPU.Build.0 = Release|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.Debug|Any CPU.Build.0 = Debug|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.Release|Any CPU.ActiveCfg = Release|Any CPU {668E9021-CE84-49D9-98FB-DF125A9FCDB0}.Release|Any CPU.Build.0 = Release|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.Build.0 = Release|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.Debug|Any CPU.Build.0 = Debug|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AF77741-9158-4D5F-8782-8F21FADF025F}.Release|Any CPU.Build.0 = Release|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1}.Release|Any CPU.Build.0 = Release|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0187BC2-8325-4BB2-8697-7F955CF4173E}.Release|Any CPU.Build.0 = Release|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.Debug|Any CPU.Build.0 = Debug|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.Release|Any CPU.ActiveCfg = Release|Any CPU {3267DFC6-B34D-4011-BC0F-D3B56AF6F608}.Release|Any CPU.Build.0 = Release|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA62944F-BD25-4730-9405-9BE8FF5BEACD}.Release|Any CPU.Build.0 = Release|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.Debug|Any CPU.Build.0 = Debug|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.Release|Any CPU.ActiveCfg = Release|Any CPU {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853}.Release|Any CPU.Build.0 = Release|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {694C6EDF-EA52-438F-B745-82B025ECC0E7}.Release|Any CPU.Build.0 = Release|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F29EE87-6A63-43C6-B7FF-74DD06815830}.Release|Any CPU.Build.0 = Release|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.Debug|Any CPU.Build.0 = Debug|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.Release|Any CPU.ActiveCfg = Release|Any CPU {FEDFE6CA-8282-4C5B-A756-E97189690982}.Release|Any CPU.Build.0 = Release|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {291EF478-BF24-4935-BC78-E0DCCD0C9A1B}.Release|Any CPU.Build.0 = Release|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E81EF98-8F5C-4EED-8B37-456991CA56FD}.Release|Any CPU.Build.0 = Release|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.Debug|Any CPU.Build.0 = Debug|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.Release|Any CPU.ActiveCfg = Release|Any CPU {F87FD911-4A97-4057-8EAE-1CB96B9A1937}.Release|Any CPU.Build.0 = Release|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B}.Release|Any CPU.Build.0 = Release|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E22245AF-D5E1-46F6-B443-C886983EC50C}.Release|Any CPU.Build.0 = Release|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.Debug|Any CPU.Build.0 = Debug|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.Release|Any CPU.ActiveCfg = Release|Any CPU {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E}.Release|Any CPU.Build.0 = Release|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.Debug|Any CPU.Build.0 = Debug|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.Release|Any CPU.ActiveCfg = Release|Any CPU {41D5691F-2720-44A0-9185-EEFE928D2679}.Release|Any CPU.Build.0 = Release|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Debug|Any CPU.Build.0 = Debug|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Release|Any CPU.ActiveCfg = Release|Any CPU {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467}.Release|Any CPU.Build.0 = Release|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {C19267DD-3984-430C-AE18-4034F85DE4E5}.Release|Any CPU.Build.0 = Release|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.Debug|Any CPU.Build.0 = Debug|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.Release|Any CPU.ActiveCfg = Release|Any CPU {43C1B979-D593-4A32-BB3A-4316F1C66D66}.Release|Any CPU.Build.0 = Release|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1}.Release|Any CPU.Build.0 = Release|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF07E947-120D-4E93-93DA-A4BF121753EA}.Release|Any CPU.Build.0 = Release|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E89A3E9-0A7F-418F-B4BE-6E38A6315373}.Release|Any CPU.Build.0 = Release|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.Debug|Any CPU.Build.0 = Debug|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.Release|Any CPU.ActiveCfg = Release|Any CPU {821A136C-7C6F-44C6-A9E6-C39B5BFB1483}.Release|Any CPU.Build.0 = Release|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3BEF382-C7C4-454D-B017-1EAC03E9A82C}.Release|Any CPU.Build.0 = Release|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.Debug|Any CPU.Build.0 = Debug|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.Release|Any CPU.ActiveCfg = Release|Any CPU {5ABD9968-F3A3-4967-B768-A6142F69759E}.Release|Any CPU.Build.0 = Release|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.Debug|Any CPU.Build.0 = Debug|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.Release|Any CPU.ActiveCfg = Release|Any CPU {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B}.Release|Any CPU.Build.0 = Release|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1A20049-04C2-4676-93CF-92449C4BBAA9}.Release|Any CPU.Build.0 = Release|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {6C320AD9-F380-4F8B-85F9-0689F88766EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {8F18041B-9410-4C36-A9C5-067813DF5F31} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {22BABB60-8F02-4027-AFFC-ACF069954536} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {9B7E3740-6161-4548-833C-4BBCA43B970E} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {4D39BAAF-8A96-473E-AB79-C8A341885137} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {55A15F40-1435-4248-A7F2-2A146BB83586} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {0C7CE809-0F72-4C19-8C64-D6573E4D9521} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {C23F02FC-4538-43F5-ABBA-38BA069AEA8F} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {3D3FFD8A-624D-4E9B-954B-E1C105507975} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {D3CF7430-6DA4-42B0-BD90-CA39D16687B2} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {268DEE9D-F323-4A00-8ED8-3784388C3E3A} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {0F4870DB-A799-4DBA-99DF-0D74BB52FEC2} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {66A74F3C-A106-4C1E-BAA0-001908FEA2CA} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {E2D008A9-4D1D-4F6B-8325-4ED717D6EA0A} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {CD48EB41-92A5-4628-A0F7-6A43DF58827E} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {2C653A66-8159-4A41-954F-A67915DFDA87} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {21C729D6-ECF8-47EF-A236-7C6A4272EAF0} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {8AC2A2E4-2F11-4D40-A887-62E2583A65E6} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {6C28DA70-60F1-4442-967F-591BF3962EC5} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {DDC1CE0C-486E-4E35-BB3B-EAB61F8F9440} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {7F2C796F-43B2-4F8F-ABFF-A154EC8AAFA1} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {668E9021-CE84-49D9-98FB-DF125A9FCDB0} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {7AF77741-9158-4D5F-8782-8F21FADF025F} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {66492E69-CE4C-4FB1-9B1F-88DEE09D06F1} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {A0187BC2-8325-4BB2-8697-7F955CF4173E} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {3267DFC6-B34D-4011-BC0F-D3B56AF6F608} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {EA62944F-BD25-4730-9405-9BE8FF5BEACD} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {694C6EDF-EA52-438F-B745-82B025ECC0E7} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {7F29EE87-6A63-43C6-B7FF-74DD06815830} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {FEDFE6CA-8282-4C5B-A756-E97189690982} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {291EF478-BF24-4935-BC78-E0DCCD0C9A1B} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {6E81EF98-8F5C-4EED-8B37-456991CA56FD} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {F87FD911-4A97-4057-8EAE-1CB96B9A1937} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {8A607AC9-E7DD-4B74-A0B1-47FC95B9838B} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {E22245AF-D5E1-46F6-B443-C886983EC50C} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {41D5691F-2720-44A0-9185-EEFE928D2679} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {66DD7CD7-C68F-4D0E-9F3D-3B58C49D1467} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {C19267DD-3984-430C-AE18-4034F85DE4E5} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {43C1B979-D593-4A32-BB3A-4316F1C66D66} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {25DEF6F6-7F99-4EB7-91ED-5181A346AFE1} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {BF07E947-120D-4E93-93DA-A4BF121753EA} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {1E89A3E9-0A7F-418F-B4BE-6E38A6315373} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {821A136C-7C6F-44C6-A9E6-C39B5BFB1483} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {C3BEF382-C7C4-454D-B017-1EAC03E9A82C} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {5ABD9968-F3A3-4967-B768-A6142F69759E} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {9AAFB58C-B8C1-4D7F-80E6-7B95C94A829B} = {A9836F9E-6DB3-4D9F-ADCA-CF42D8C8BA93} {A1A20049-04C2-4676-93CF-92449C4BBAA9} = {C40883CD-366D-4534-8B58-3EA0D13136DF} {6C320AD9-F380-4F8B-85F9-0689F88766EC} = {C40883CD-366D-4534-8B58-3EA0D13136DF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A855CFDC-9BEE-43A9-A3EA-4C4624A747DB} EndGlobalSection EndGlobal ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Reporting a Vulnerability Security issues and bugs should be reported privately to the Microsoft Security Response Center (MSRC), either by emailing secure@microsoft.com or via the portal at https://msrc.microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [MSRC Report an Issue FAQ](https://www.microsoft.com/en-us/msrc/faqs-report-an-issue). Please do not open issues for anything you think might have a security implication. ================================================ FILE: Settings.StyleCop ================================================ NoMerge False False as db dc do ef id if in is my no on sl to ui vs False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False True True False False False False False False False False False False False False False False False False False False False False Copyright (c) .NET Foundation. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. ================================================ FILE: Tools.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27016.1 MinimumVisualStudioVersion = 15.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.FxCop", "tools\src\Microsoft.Web.FxCop\Microsoft.Web.FxCop.csproj", "{F439D4E6-3FAC-4C30-9585-6D258133A2BF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F439D4E6-3FAC-4C30-9585-6D258133A2BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F439D4E6-3FAC-4C30-9585-6D258133A2BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {F439D4E6-3FAC-4C30-9585-6D258133A2BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {F439D4E6-3FAC-4C30-9585-6D258133A2BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F8728741-0321-4EF6-9359-7DF2DCE6E99E} EndGlobalSection EndGlobal ================================================ FILE: azure-pipelines-public.yml ================================================ parameters: # Test only the Release build by default. - name: ReleaseBuildTarget displayName: 'Build which target for Release?' type: string values: [ Build, Integration, UnitTest ] default: UnitTest - name: OtherBuildTarget displayName: 'Build which target for Debug/CodeAnalysis?' type: string values: [ Build, Integration, UnitTest ] default: Build variables: - name: DOTNET_CLI_TELEMETRY_OPTOUT value: 1 - name: DOTNET_NOLOGO value: 1 # Run CodeQL3000 tasks in a separate internal pipeline; not needed here. - name: Codeql.SkipTaskAutoInjection value: true trigger: [main] pr: ['*'] stages: - stage: build displayName: Build jobs: - template: /eng/templates/default-build.yml parameters: ReleaseBuildTarget: ${{ parameters.ReleaseBuildTarget }} OtherBuildTarget: ${{ parameters.OtherBuildTarget }} ================================================ FILE: azure-pipelines.yml ================================================ parameters: # Test only the Release build by default. - name: ReleaseBuildTarget displayName: 'Build which target for Release?' type: string values: [ Build, Integration, UnitTest ] default: Build - name: OtherBuildTarget displayName: 'Build which target for Debug/CodeAnalysis?' type: string values: [ Build, Integration, UnitTest ] default: Build variables: - name: DOTNET_CLI_TELEMETRY_OPTOUT value: 1 - name: DOTNET_NOLOGO value: 1 # Run CodeQL3000 tasks in a separate internal pipeline; not needed here. - name: Codeql.SkipTaskAutoInjection value: true trigger: [main] pr: ['*'] resources: repositories: # Repo: 1ESPipelineTemplates/1ESPipelineTemplates - repository: 1esPipelines type: git name: 1ESPipelineTemplates/1ESPipelineTemplates ref: refs/tags/release extends: template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines parameters: settings: networkIsolationPolicy: Permissive, CFSClean, CFSClean2 sdl: policheck: enabled: true tsa: enabled: true pool: name: NetCore1ESPool-Svc-Internal demands: - ImageOverride -equals windows.vs2019.amd64 - ImageVersionOverride -equals 2026.0304.014948 os: windows stages: - stage: build displayName: Build jobs: - template: /eng/templates/default-build.yml@self parameters: ReleaseBuildTarget: ${{ parameters.ReleaseBuildTarget }} OtherBuildTarget: ${{ parameters.OtherBuildTarget }} ================================================ FILE: build.cmd ================================================ @echo off setlocal if exist bin goto Build mkdir bin :Build REM Require VS2019 (v16.0) on the system. Use `vswhere` for the search because it can find all VS installations. set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" if not exist %vswhere% ( set vswhere="%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe" ) if not exist %vswhere% ( REM vswhere.exe not in normal locations; check the Path. for %%X in (vswhere.exe) do ( set vswhere="%%~$PATH:X" ) ) if not exist %vswhere% ( echo Could not find vswhere.exe. Please run this from a Visual Studio developer prompt. goto BuildFail ) set InstallDir= for /f "usebackq tokens=*" %%i in (`%vswhere% -version 16 -latest -prerelease -products * ^ -requires Microsoft.Net.Component.4.5.TargetingPack ^ -requires Microsoft.Net.Component.4.5.2.TargetingPack ^ -requires Microsoft.Net.Component.4.6.2.TargetingPack ^ -property installationPath`) do ( set "InstallDir=%%i" ) if not DEFINED InstallDir ( echo "Could not find a VS2019 installation with the necessary components (targeting packs for v4.5, v4.5.2, and v4.6.2)." echo Please install VS2019 or the missing components. goto BuildFail ) REM Find a 64bit MSBuild and add it to path. Require v17.8.3 or later due to our .NET SDK choice. REM Check for VS2022 first. set InstallDir= for /f "usebackq tokens=*" %%i in (`%vswhere% -version 17.8.3 -latest -prerelease -products * ^ -requires Microsoft.Component.MSBuild ^ -property installationPath`) do ( set "InstallDir=%%i" ) if DEFINED InstallDir ( REM Add MSBuild to the path. set "PATH=%InstallDir%\MSBuild\Current\Bin;%PATH%" goto FoundMSBuild ) REM Otherwise find or install an xcopy-able MSBuild. echo "Could not find a VS2022 installation with the necessary components (MSBuild). Falling back..." set "MSBuildVersion=17.8.5" set "Command=[System.Threading.Thread]::CurrentThread.CurrentCulture = ''" set "Command=%Command%; [System.Threading.Thread]::CurrentThread.CurrentUICulture = ''" set "Command=%Command%; try { & '%~dp0eng\GetXCopyMSBuild.ps1' %MSBuildVersion%; exit $LASTEXITCODE }" set "Command=%Command% catch { write-host $_; exit 1 }" PowerShell -NoProfile -NoLogo -ExecutionPolicy Bypass -Command "%Command%" if %ERRORLEVEL% neq 0 goto BuildFail REM Add MSBuild to the path. set "PATH=%~dp0.msbuild\%MSBuildVersion%\tools\MSBuild\Current\Bin;%PATH%" :FoundMSBuild REM Configure NuGet operations to work w/in this repo i.e. do not pollute system packages folder. REM Note this causes two copies of packages restored using packages.config to land in this folder e.g. REM StyleCpy.5.0.0/ and stylecop/5.0.0/. set "NUGET_PACKAGES=%~dp0packages" REM Are we running in a local dev environment (not on CI)? if DEFINED CI (set Desktop=false) else if DEFINED TEAMCITY_VERSION (set Desktop=false) else (set Desktop=true) pushd %~dp0 if "%1" == "" goto BuildDefaults MSBuild "%~dp0Runtime.msbuild" /m /nr:false /p:Platform="Any CPU" /p:Desktop=%Desktop% /v:M ^ /fl /fileLoggerParameters:LogFile=bin\msbuild.log;Verbosity=Normal /consoleLoggerParameters:Summary /t:%* if %ERRORLEVEL% neq 0 goto BuildFail goto BuildSuccess :BuildDefaults MSBuild "%~dp0Runtime.msbuild" /m /nr:false /p:Platform="Any CPU" /p:Desktop=%Desktop% /v:M ^ /fl /fileLoggerParameters:LogFile=bin\msbuild.log;Verbosity=Normal /consoleLoggerParameters:Summary if %ERRORLEVEL% neq 0 goto BuildFail goto BuildSuccess :BuildFail echo. echo *** BUILD FAILED *** popd endlocal exit /B 999 :BuildSuccess echo. echo **** BUILD SUCCESSFUL *** popd endlocal exit /B 0 ================================================ FILE: eng/GetXCopyMSBuild.ps1 ================================================ # Lifted from https://github.com/dotnet/arcade/blob/main/eng/common/tools.ps1 [CmdletBinding(DefaultParameterSetName='Groups')] param( [string]$Version = '17.4.1' ) Set-StrictMode -Version 2 $ErrorActionPreference = 'Stop' function Create-Directory ([string[]] $path) { New-Item -Path $path -Force -ItemType 'Directory' | Out-Null } function Unzip([string]$zipfile, [string]$outpath) { Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) } function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install, [string]$ToolsDir) { $packageName = 'RoslynTools.MSBuild' $packageDir = Join-Path $ToolsDir $packageVersion $packagePath = Join-Path $packageDir "$packageName.$packageVersion.nupkg" if (!(Test-Path $packageDir)) { if (!$install) { return $null } Create-Directory $packageDir Write-Host "Downloading $packageName $packageVersion" $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -OutFile $packagePath Unzip $packagePath $packageDir } return Join-Path $packageDir 'tools' } $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\') InitializeXCopyMSBuild -packageVersion $Version -install $true -ToolsDir (join-path $RepoRoot .msbuild) ================================================ FILE: eng/templates/default-build.yml ================================================ parameters: ReleaseBuildTarget: '' OtherBuildTarget: '' jobs: - job: build displayName: Build ${{ if eq(variables['System.TeamProject'], 'public') }}: pool: name: NetCore-Svc-Public demands: - ImageOverride -equals windows.vs2019.amd64.open timeoutInMinutes: 30 strategy: matrix: Release: _BuildTarget: ${{ parameters.ReleaseBuildTarget }} _Configuration: Release _StyleCopEnabled: true # Do CG work only in internal pipelines. skipComponentGovernanceDetection: ${{ eq(variables['System.TeamProject'], 'public') }} Debug: _BuildTarget: ${{ parameters.OtherBuildTarget }} _Configuration: Debug _StyleCopEnabled: false # Do not redo CG work. Configuration changes in this part of the matrix are not relevant to CG. skipComponentGovernanceDetection: true CodeAnalysis: _BuildTarget: ${{ parameters.OtherBuildTarget }} _Configuration: CodeAnalysis _StyleCopEnabled: false # Do not redo CG work. Configuration changes in this part of the matrix are not relevant to CG. skipComponentGovernanceDetection: true steps: - checkout: self clean: true displayName: Checkout - task: UseDotNet@2 displayName: Get .NET SDK inputs: useGlobalJson: true - task: UseDotNet@2 displayName: Get .NET 2.1 runtime inputs: packageType: runtime version: '2.1.x' - script: .\build.cmd EnableSkipStrongNames displayName: Enable SkipStrongNames - script: .\build.cmd $(_BuildTarget) ^ /binaryLogger:artifacts/msbuild.binlog /p:Configuration=$(_Configuration) /p:StyleCopEnabled=$(_StyleCopEnabled) ^ /fileLoggerParameters:LogFile=artifacts/msbuild.log;Summary;Verbosity=minimal displayName: Build - ${{ if eq(variables['System.TeamProject'], 'public') }}: - task: PublishBuildArtifacts@1 displayName: Upload test results condition: and(always(), ne(variables._BuildTarget, 'Build')) continueOnError: true inputs: pathtoPublish: ./bin/$(_Configuration)/Test/TestResults/ artifactName: $(_Configuration) Test Results $(System.JobId) artifactType: Container parallel: true - task: PublishTestResults@2 condition: and(always(), ne(variables._BuildTarget, 'Build')) continueOnError: true displayName: Publish test results inputs: mergeTestResults: true searchFolder: ./bin/$(_Configuration)/Test/TestResults/ testResultsFiles: '*.xml' testRunner: xUnit testRunTitle: $(_Configuration) - task: PublishBuildArtifacts@1 displayName: Upload logs condition: always() continueOnError: true inputs: pathtoPublish: ./artifacts/ artifactName: $(_Configuration) Logs $(System.JobId) artifactType: Container parallel: true - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - task: 1ES.PublishPipelineArtifact@1 inputs: condition: and(always(), ne(variables._BuildTarget, 'Build')) path: ./bin/$(_Configuration)/Test/TestResults/ artifact: $(_Configuration) Test Results $(System.JobId) - task: 1ES.PublishPipelineArtifact@1 inputs: path: ./artifacts/ artifact: $(_Configuration) Logs $(System.JobId) ================================================ FILE: es-metadata.yml ================================================ schemaVersion: 0.0.1 isProduction: true accountableOwners: service: 4db45fa9-fb0f-43ce-b523-ad1da773dfbc routing: defaultAreaPath: org: devdiv path: DevDiv\ASP.NET Core ================================================ FILE: global.json ================================================ { "sdk": { "version": "8.0.419", "rollForward": "major" } } ================================================ FILE: src/CodeAnalysisDictionary.xml ================================================  Multi Bitly Digg Facebook Reddit Captcha Facebook Gravatar JSON Lookahead MVC Param Params Pluralizer Pragma Pragmas Templating Unvalidated Validator Validators Validatable WebPage cshtml vbhtml asax Eval Src Charset Coords Rel Dto Tokenizer ReDim OAuth OpenID Yadis fwlink Edm Deserializer Api ws enc dir Auth bg Cors Owin Unbuffered Rfc Realtime ModelName BSON Untyped Behavior Callback Canceled Color Fallback Markup Preflight WebPage WebPages TimeLine oAuth userName modelName HasId ID Db Dto ================================================ FILE: src/Common/AttributeList.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace System.ComponentModel { /// /// Wrapper for AttributeCollection to provide generic collection implementation. /// internal sealed class AttributeList : IList { private readonly AttributeCollection _attributes; public AttributeList(AttributeCollection attributes) { Contract.Assert(attributes != null); _attributes = attributes; } public int Count { get { return _attributes.Count; } } public bool IsReadOnly { get { return true; } } public Attribute this[int index] { get { return _attributes[index]; } set { throw new NotSupportedException(); } } public void Add(Attribute attribute) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(Attribute attribute) { return _attributes.Contains(attribute); } public void CopyTo(Attribute[] target, int startIndex) { _attributes.CopyTo(target, startIndex); } public IEnumerator GetEnumerator() { for (int i = 0; i < _attributes.Count; i++) { yield return _attributes[i]; } } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_attributes).GetEnumerator(); } public int IndexOf(Attribute attribute) { for (int i = 0; i < _attributes.Count; i++) { if (attribute == _attributes[i]) { return i; } } return -1; } public void Insert(int index, Attribute attribute) { throw new NotSupportedException(); } bool ICollection.Remove(Attribute attribute) { throw new NotSupportedException(); } public void RemoveAt(int index) { throw new NotSupportedException(); } } } ================================================ FILE: src/Common/CollectionExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Linq; namespace System.Collections.Generic { /// /// Helper extension methods for fast use of collections. /// internal static class CollectionExtensions { /// /// Return a new array with the value added to the end. Slow and best suited to long lived arrays with few writes relative to reads. /// public static T[] AppendAndReallocate(this T[] array, T value) { Contract.Assert(array != null); int originalLength = array.Length; T[] newArray = new T[originalLength + 1]; array.CopyTo(newArray, 0); newArray[originalLength] = value; return newArray; } /// /// Return the enumerable as an Array, copying if required. Optimized for common case where it is an Array. /// Avoid mutating the return value. /// public static T[] AsArray(this IEnumerable values) { Contract.Assert(values != null); T[] array = values as T[]; if (array == null) { array = values.ToArray(); } return array; } /// /// Return the enumerable as a Collection of T, copying if required. Optimized for the common case where it is /// a Collection of T and avoiding a copy if it implements IList of T. Avoid mutating the return value. /// public static Collection AsCollection(this IEnumerable enumerable) { Contract.Assert(enumerable != null); Collection collection = enumerable as Collection; if (collection != null) { return collection; } // Check for IList so that collection can wrap it instead of copying IList list = enumerable as IList; if (list == null) { list = new List(enumerable); } return new Collection(list); } /// /// Return the enumerable as a IList of T, copying if required. Avoid mutating the return value. /// public static IList AsIList(this IEnumerable enumerable) { Contract.Assert(enumerable != null); IList list = enumerable as IList; if (list != null) { return list; } return new List(enumerable); } /// /// Return the enumerable as a List of T, copying if required. Optimized for common case where it is an List of T /// or a ListWrapperCollection of T. Avoid mutating the return value. /// public static List AsList(this IEnumerable enumerable) { Contract.Assert(enumerable != null); List list = enumerable as List; if (list != null) { return list; } ListWrapperCollection listWrapper = enumerable as ListWrapperCollection; if (listWrapper != null) { return listWrapper.ItemsList; } return new List(enumerable); } /// /// Remove values from the list starting at the index start. /// public static void RemoveFrom(this List list, int start) { Contract.Assert(list != null); Contract.Assert(start >= 0 && start <= list.Count); list.RemoveRange(start, list.Count - start); } /// /// Return the only value from list, the type's default value if empty, or call the errorAction for 2 or more. /// public static T SingleDefaultOrError(this IList list, Action errorAction, TArg1 errorArg1) { Contract.Assert(list != null); Contract.Assert(errorAction != null); switch (list.Count) { case 0: return default(T); case 1: T value = list[0]; return value; default: errorAction(errorArg1); return default(T); } } /// /// Returns a single value in list matching type TMatch if there is only one, null if there are none of type TMatch or calls the /// errorAction with errorArg1 if there is more than one. /// public static TMatch SingleOfTypeDefaultOrError(this IList list, Action errorAction, TArg1 errorArg1) where TMatch : class { Contract.Assert(list != null); Contract.Assert(errorAction != null); TMatch result = null; for (int i = 0; i < list.Count; i++) { TMatch typedValue = list[i] as TMatch; if (typedValue != null) { if (result == null) { result = typedValue; } else { errorAction(errorArg1); return null; } } } return result; } /// /// Convert an ICollection to an array, removing null values. Fast path for case where there are no null values. /// public static T[] ToArrayWithoutNulls(this ICollection collection) where T : class { Contract.Assert(collection != null); T[] result = new T[collection.Count]; int count = 0; foreach (T value in collection) { if (value != null) { result[count] = value; count++; } } if (count == collection.Count) { return result; } else { T[] trimmedResult = new T[count]; Array.Copy(result, trimmedResult, count); return trimmedResult; } } /// /// Convert the array to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for array input. /// public static Dictionary ToDictionaryFast(this TValue[] array, Func keySelector, IEqualityComparer comparer) { Contract.Assert(array != null); Contract.Assert(keySelector != null); Dictionary dictionary = new Dictionary(array.Length, comparer); for (int i = 0; i < array.Length; i++) { TValue value = array[i]; dictionary.Add(keySelector(value), value); } return dictionary; } /// /// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input with fast path for array. /// public static Dictionary ToDictionaryFast(this IList list, Func keySelector, IEqualityComparer comparer) { Contract.Assert(list != null); Contract.Assert(keySelector != null); TValue[] array = list as TValue[]; if (array != null) { return ToDictionaryFast(array, keySelector, comparer); } return ToDictionaryFastNoCheck(list, keySelector, comparer); } /// /// Convert the enumerable to a Dictionary using the keySelector to extract keys from values and the specified comparer. Fast paths for array and IList of T. /// public static Dictionary ToDictionaryFast(this IEnumerable enumerable, Func keySelector, IEqualityComparer comparer) { Contract.Assert(enumerable != null); Contract.Assert(keySelector != null); TValue[] array = enumerable as TValue[]; if (array != null) { return ToDictionaryFast(array, keySelector, comparer); } IList list = enumerable as IList; if (list != null) { return ToDictionaryFastNoCheck(list, keySelector, comparer); } Dictionary dictionary = new Dictionary(comparer); foreach (TValue value in enumerable) { dictionary.Add(keySelector(value), value); } return dictionary; } /// /// Convert the list to a Dictionary using the keySelector to extract keys from values and the specified comparer. Optimized for IList of T input. No checking for other types. /// private static Dictionary ToDictionaryFastNoCheck(IList list, Func keySelector, IEqualityComparer comparer) { Contract.Assert(list != null); Contract.Assert(keySelector != null); int listCount = list.Count; Dictionary dictionary = new Dictionary(listCount, comparer); for (int i = 0; i < listCount; i++) { TValue value = list[i]; dictionary.Add(keySelector(value), value); } return dictionary; } } } ================================================ FILE: src/Common/CommonWebApiResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.Http.Properties { using System; using System.Linq; using System.Reflection; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class CommonWebApiResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal CommonWebApiResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { #if NETSTANDARD1_3 var assembly = typeof(CommonWebApiResources).GetTypeInfo().Assembly; #else var assembly = typeof(CommonWebApiResources).Assembly; #endif // Find the CommonResources.resources file's full resource name in this assembly string commonResourcesName = assembly.GetManifestResourceNames().Where(s => s.EndsWith("CommonWebApiResources.resources", StringComparison.OrdinalIgnoreCase)).Single(); // Trim off the ".resources" commonResourcesName = commonResourcesName.Substring(0, commonResourcesName.Length - 10); // Load the resource manager global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(commonResourcesName, assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Relative URI values are not supported: '{0}'. The URI must be absolute.. /// internal static string ArgumentInvalidAbsoluteUri { get { return ResourceManager.GetString("ArgumentInvalidAbsoluteUri", resourceCulture); } } /// /// Looks up a localized string similar to Unsupported URI scheme: '{0}'. The URI scheme must be either '{1}' or '{2}'.. /// internal static string ArgumentInvalidHttpUriScheme { get { return ResourceManager.GetString("ArgumentInvalidHttpUriScheme", resourceCulture); } } /// /// Looks up a localized string similar to Value must be greater than or equal to {0}.. /// internal static string ArgumentMustBeGreaterThanOrEqualTo { get { return ResourceManager.GetString("ArgumentMustBeGreaterThanOrEqualTo", resourceCulture); } } /// /// Looks up a localized string similar to Value must be less than or equal to {0}.. /// internal static string ArgumentMustBeLessThanOrEqualTo { get { return ResourceManager.GetString("ArgumentMustBeLessThanOrEqualTo", resourceCulture); } } /// /// Looks up a localized string similar to The argument '{0}' is null or empty.. /// internal static string ArgumentNullOrEmpty { get { return ResourceManager.GetString("ArgumentNullOrEmpty", resourceCulture); } } /// /// Looks up a localized string similar to URI must not contain a query component or a fragment identifier.. /// internal static string ArgumentUriHasQueryOrFragment { get { return ResourceManager.GetString("ArgumentUriHasQueryOrFragment", resourceCulture); } } /// /// Looks up a localized string similar to The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.. /// internal static string InvalidEnumArgument { get { return ResourceManager.GetString("InvalidEnumArgument", resourceCulture); } } } } ================================================ FILE: src/Common/CommonWebApiResources.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Relative URI values are not supported: '{0}'. The URI must be absolute. Unsupported URI scheme: '{0}'. The URI scheme must be either '{1}' or '{2}'. Value must be greater than or equal to {0}. Value must be less than or equal to {0}. The argument '{0}' is null or empty. URI must not contain a query component or a fragment identifier. The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. ================================================ FILE: src/Common/DictionaryExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics.Contracts; namespace System.Collections.Generic { /// /// Extension methods for . /// [EditorBrowsable(EditorBrowsableState.Never)] internal static class DictionaryExtensions { /// /// Remove entries from dictionary that match the removeCondition. /// public static void RemoveFromDictionary(this IDictionary dictionary, Func, bool> removeCondition) { // Pass the delegate as the state to avoid a delegate and closure dictionary.RemoveFromDictionary((entry, innerCondition) => { return innerCondition(entry); }, removeCondition); } /// /// Remove entries from dictionary that match the removeCondition. /// public static void RemoveFromDictionary(this IDictionary dictionary, Func, TState, bool> removeCondition, TState state) { Contract.Assert(dictionary != null); Contract.Assert(removeCondition != null); // Because it is not possible to delete while enumerating, a copy of the keys must be taken. Use the size of the dictionary as an upper bound // to avoid creating more than one copy of the keys. int removeCount = 0; TKey[] keys = new TKey[dictionary.Count]; foreach (var entry in dictionary) { if (removeCondition(entry, state)) { keys[removeCount] = entry.Key; removeCount++; } } for (int i = 0; i < removeCount; i++) { dictionary.Remove(keys[i]); } } /// /// Gets the value of associated with the specified key or default value if /// either the key is not present or the value is not of type . /// /// The type of the value associated with the specified key. /// The instance where TValue is object. /// The key whose value to get. /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. /// true if key was found, value is non-null, and value is of type ; otherwise false. public static bool TryGetValue(this IDictionary collection, string key, out T value) { Contract.Assert(collection != null); object valueObj; if (collection.TryGetValue(key, out valueObj)) { if (valueObj is T) { value = (T)valueObj; return true; } } value = default(T); return false; } internal static IEnumerable> FindKeysWithPrefix(this IDictionary dictionary, string prefix) { Contract.Assert(dictionary != null); Contract.Assert(prefix != null); TValue exactMatchValue; if (dictionary.TryGetValue(prefix, out exactMatchValue)) { yield return new KeyValuePair(prefix, exactMatchValue); } foreach (var entry in dictionary) { string key = entry.Key; if (key.Length <= prefix.Length) { continue; } if (!key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { continue; } // Everything is prefixed by the empty string if (prefix.Length == 0) { yield return entry; } else { char charAfterPrefix = key[prefix.Length]; switch (charAfterPrefix) { case '[': case '.': yield return entry; break; } } } } } } ================================================ FILE: src/Common/EfficientTypePropertyKey.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Http { // When the key is cached, it will be more efficient that a normal tuple, because the hash code call // is rather expensive particularly for T as Type or T as long string. internal class EfficientTypePropertyKey : Tuple { private int _hashCode; public EfficientTypePropertyKey(T1 item1, T2 item2) : base(item1, item2) { _hashCode = base.GetHashCode(); } public override int GetHashCode() { return _hashCode; } } } ================================================ FILE: src/Common/Empty.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Collections.Generic { /// /// Helper to provide empty instances with minimal allocation. /// internal static class Empty { private static readonly T[] _emptyArray = new T[0]; /// /// Returns a zero length array of type. Only allocates once per distinct type. /// public static T[] Array { get { return _emptyArray; } } } } ================================================ FILE: src/Common/Error.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Web.Http.Properties; namespace System.Web.Http { /// /// Utility class for creating and unwrapping instances. /// internal static class Error { private const string HttpScheme = "http"; private const string HttpsScheme = "https"; /// /// Formats the specified resource string using . /// /// A composite format string. /// An object array that contains zero or more objects to format. /// The formatted string. internal static string Format(string format, params object[] args) { return String.Format(CultureInfo.CurrentCulture, format, args); } /// /// Creates an with the provided properties. /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static ArgumentException Argument(string messageFormat, params object[] messageArgs) { return new ArgumentException(Error.Format(messageFormat, messageArgs)); } /// /// Creates an with the provided properties. /// /// The name of the parameter that caused the current exception. /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static ArgumentException Argument(string parameterName, string messageFormat, params object[] messageArgs) { return new ArgumentException(Error.Format(messageFormat, messageArgs), parameterName); } /// /// Creates an with a message saying that the argument must be an "http" or "https" URI. /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// The logged . internal static ArgumentException ArgumentUriNotHttpOrHttpsScheme(string parameterName, Uri actualValue) { return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentInvalidHttpUriScheme, actualValue, HttpScheme, HttpsScheme), parameterName); } /// /// Creates an with a message saying that the argument must be an absolute URI. /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// The logged . internal static ArgumentException ArgumentUriNotAbsolute(string parameterName, Uri actualValue) { return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentInvalidAbsoluteUri, actualValue), parameterName); } /// /// Creates an with a message saying that the argument must be an absolute URI /// without a query or fragment identifier and then logs it with . /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// The logged . internal static ArgumentException ArgumentUriHasQueryOrFragment(string parameterName, Uri actualValue) { return new ArgumentException(Error.Format(CommonWebApiResources.ArgumentUriHasQueryOrFragment, actualValue), parameterName); } /// /// Creates an with the provided properties. /// /// The logged . [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this API is to return an error for properties")] internal static ArgumentNullException PropertyNull() { return new ArgumentNullException("value"); } /// /// Creates an with the provided properties. /// /// The name of the parameter that caused the current exception. /// The logged . internal static ArgumentNullException ArgumentNull(string parameterName) { return new ArgumentNullException(parameterName); } /// /// Creates an with the provided properties. /// /// The name of the parameter that caused the current exception. /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static ArgumentNullException ArgumentNull(string parameterName, string messageFormat, params object[] messageArgs) { return new ArgumentNullException(parameterName, Error.Format(messageFormat, messageArgs)); } /// /// Creates an with a default message. /// /// The name of the parameter that caused the current exception. /// The logged . internal static ArgumentException ArgumentNullOrEmpty(string parameterName) { return Error.Argument(parameterName, CommonWebApiResources.ArgumentNullOrEmpty, parameterName); } /// /// Creates an with the provided properties. /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static ArgumentOutOfRangeException ArgumentOutOfRange(string parameterName, object actualValue, string messageFormat, params object[] messageArgs) { return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(messageFormat, messageArgs)); } /// /// Creates an with a message saying that the argument must be greater than or equal to . /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// The minimum size of the argument. /// The logged . internal static ArgumentOutOfRangeException ArgumentMustBeGreaterThanOrEqualTo(string parameterName, object actualValue, object minValue) { return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(CommonWebApiResources.ArgumentMustBeGreaterThanOrEqualTo, minValue)); } /// /// Creates an with a message saying that the argument must be less than or equal to . /// /// The name of the parameter that caused the current exception. /// The value of the argument that causes this exception. /// The maximum size of the argument. /// The logged . internal static ArgumentOutOfRangeException ArgumentMustBeLessThanOrEqualTo(string parameterName, object actualValue, object maxValue) { return new ArgumentOutOfRangeException(parameterName, actualValue, Error.Format(CommonWebApiResources.ArgumentMustBeLessThanOrEqualTo, maxValue)); } /// /// Creates an with a message saying that the key was not found. /// /// The logged . internal static KeyNotFoundException KeyNotFound() { return new KeyNotFoundException(); } /// /// Creates an with a message saying that the key was not found. /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static KeyNotFoundException KeyNotFound(string messageFormat, params object[] messageArgs) { return new KeyNotFoundException(Error.Format(messageFormat, messageArgs)); } /// /// Creates an initialized according to guidelines. /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static ObjectDisposedException ObjectDisposed(string messageFormat, params object[] messageArgs) { // Pass in null, not disposedObject.GetType().FullName as per the above guideline return new ObjectDisposedException(null, Error.Format(messageFormat, messageArgs)); } /// /// Creates an initialized with the provided parameters. /// /// The logged . internal static OperationCanceledException OperationCanceled() { return new OperationCanceledException(); } /// /// Creates an initialized with the provided parameters. /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static OperationCanceledException OperationCanceled(string messageFormat, params object[] messageArgs) { return new OperationCanceledException(Error.Format(messageFormat, messageArgs)); } /// /// Creates an for an invalid enum argument. /// /// The name of the parameter that caused the current exception. /// The value of the argument that failed. /// A that represents the enumeration class with the valid values. /// The logged . internal static ArgumentException InvalidEnumArgument(string parameterName, int invalidValue, Type enumClass) { return new InvalidEnumArgumentException(parameterName, invalidValue, enumClass); } /// /// Creates an . /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static InvalidOperationException InvalidOperation(string messageFormat, params object[] messageArgs) { return new InvalidOperationException(Error.Format(messageFormat, messageArgs)); } /// /// Creates an . /// /// Inner exception /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static InvalidOperationException InvalidOperation(Exception innerException, string messageFormat, params object[] messageArgs) { return new InvalidOperationException(Error.Format(messageFormat, messageArgs), innerException); } /// /// Creates an . /// /// A composite format string explaining the reason for the exception. /// An object array that contains zero or more objects to format. /// The logged . internal static NotSupportedException NotSupported(string messageFormat, params object[] messageArgs) { return new NotSupportedException(Error.Format(messageFormat, messageArgs)); } #if NETSTANDARD1_3 // InvalidEnumArgumentException not available in netstandard1.3. internal class InvalidEnumArgumentException : ArgumentException { public InvalidEnumArgumentException() : this(null) { } public InvalidEnumArgumentException(string message) : base(message) { } public InvalidEnumArgumentException(string message, Exception innerException) : base(message, innerException) { } public InvalidEnumArgumentException(string argumentName, int invalidValue, Type enumClass) : base( Error.Format(CommonWebApiResources.InvalidEnumArgument, argumentName, invalidValue, enumClass.Name), argumentName) { } } #endif } } ================================================ FILE: src/Common/HashCodeCombiner.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; namespace Microsoft.Internal.Web.Utils { internal class HashCodeCombiner { private long _combinedHash64 = 0x1505L; public int CombinedHash { get { return _combinedHash64.GetHashCode(); } } public HashCodeCombiner Add(IEnumerable e) { if (e == null) { Add(0); } else { int count = 0; foreach (object o in e) { Add(o); count++; } Add(count); } return this; } public HashCodeCombiner Add(int i) { _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; return this; } public HashCodeCombiner Add(object o) { int hashCode = (o != null) ? o.GetHashCode() : 0; Add(hashCode); return this; } public static HashCodeCombiner Start() { return new HashCodeCombiner(); } } } ================================================ FILE: src/Common/HttpMethodHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http; namespace System.Web.Http { /// /// Various helper methods for the static members of . /// internal static class HttpMethodHelper { /// /// Gets the static instance for any given HTTP method name. /// /// The HTTP request method. /// An existing static or a new instance if the method was not found. internal static HttpMethod GetHttpMethod(string method) { if (String.IsNullOrEmpty(method)) { return null; } if (String.Equals("GET", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Get; } if (String.Equals("POST", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Post; } if (String.Equals("PUT", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Put; } if (String.Equals("DELETE", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Delete; } if (String.Equals("HEAD", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Head; } if (String.Equals("OPTIONS", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Options; } if (String.Equals("TRACE", method, StringComparison.OrdinalIgnoreCase)) { return HttpMethod.Trace; } return new HttpMethod(method); } } } ================================================ FILE: src/Common/ListWrapperCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace System.Collections.ObjectModel { /// /// A class that inherits from Collection of T but also exposes its underlying data as List of T for performance. /// internal sealed class ListWrapperCollection : Collection { private readonly List _items; internal ListWrapperCollection() : this(new List()) { } internal ListWrapperCollection(List list) : base(list) { _items = list; } internal List ItemsList { get { return _items; } } } } ================================================ FILE: src/Common/NonOwnedStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using System.Threading.Tasks; namespace System.Web.Http { /// Represents a stream that replaces another stream to prevent actually closing that stream. /// /// This class uses the Decorator [GoF] pattern; it forwards all calls except those related to Dispose and Close. /// internal class NonOwnedStream : Stream { protected NonOwnedStream() { } public NonOwnedStream(Stream innerStream) { if (innerStream == null) { throw new ArgumentNullException("innerStream"); } InnerStream = innerStream; } protected Stream InnerStream { get; set; } protected bool IsDisposed { get; private set; } public override bool CanRead { get { // Per documentation, CanRead should return false rather than throw when the stream is closed. if (IsDisposed) { return false; } return InnerStream.CanRead; } } public override bool CanSeek { get { // Per documentation, CanSeek should return false rather than throw when the stream is closed. if (IsDisposed) { return false; } return InnerStream.CanSeek; } } public override bool CanTimeout { get { // Per documentation, this value apparently is a constant for a particular implementation class. // Throwing when disposed appears inappropriate here. return InnerStream.CanTimeout; } } public override bool CanWrite { get { // Per documentation, CanWrite should return false rather than throw when the stream is closed. if (IsDisposed) { return false; } return InnerStream.CanWrite; } } public override long Length { get { // Per documentation, this property throws ObjectDisposedException if the stream is closed. ThrowIfDisposed(); return InnerStream.Length; } } public override long Position { get { // Per documentation, this property throws ObjectDisposedException if the stream is closed. ThrowIfDisposed(); return InnerStream.Position; } set { // Per documentation, this property throws ObjectDisposedException if the stream is closed. ThrowIfDisposed(); InnerStream.Position = value; } } public override int ReadTimeout { get { // Documentation does not state the behavior when the stream is closed. The NetworkStream // implementation suggests the contract should be to throw ObjectDisposedException when the stream is // closed. ThrowIfDisposed(); return InnerStream.ReadTimeout; } set { // Documentation does not state the behavior when the stream is closed. The NetworkStream // implementation suggests the contract should be to throw ObjectDisposedException when the stream is // closed. ThrowIfDisposed(); InnerStream.ReadTimeout = value; } } public override int WriteTimeout { get { // Documentation does not state the behavior when the stream is closed. The NetworkStream // implementation suggests the contract should be to throw ObjectDisposedException when the stream is // closed. ThrowIfDisposed(); return InnerStream.WriteTimeout; } set { // Documentation does not state the behavior when the stream is closed. The NetworkStream // implementation suggests the contract should be to throw ObjectDisposedException when the stream is // closed. ThrowIfDisposed(); InnerStream.WriteTimeout = value; } } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return InnerStream.BeginRead(buffer, offset, count, callback, state); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { ThrowIfDisposed(); return InnerStream.BeginWrite(buffer, offset, count, callback, state); } public override void Close() { // base.Close() calls Dispose(true) and GC.SuppressFinalize(this), which is exactly what we want. // Note that we do NOT call _innerStream.Close here, as that would actually close the original source // stream, which is the one thing this class is designed to prevent. base.Close(); } public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { ThrowIfDisposed(); return InnerStream.CopyToAsync(destination, bufferSize, cancellationToken); } // Not overriding MarshalByRefObj.CreateObjRef. // Not overriding Stream.CreateWaitHandle. [SuppressMessage( "Microsoft.Usage", "CA2215:Dispose methods should call base class dispose", Justification = "We're intentionally preventing a double dispose here.")] protected override void Dispose(bool disposing) { // Note that we do NOT call _innerStream.Dispose or Close here, as that would actually close the original // source stream, which is the one thing this class is designed to prevent. if (!IsDisposed) { base.Dispose(disposing); IsDisposed = true; } } public override int EndRead(IAsyncResult asyncResult) { ThrowIfDisposed(); return InnerStream.EndRead(asyncResult); } public override void EndWrite(IAsyncResult asyncResult) { ThrowIfDisposed(); InnerStream.EndWrite(asyncResult); } // Not overriding Object.Equals. public override void Flush() { ThrowIfDisposed(); InnerStream.Flush(); } public override Task FlushAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); return InnerStream.FlushAsync(cancellationToken); } // Not overriding Object.GetHashCode. // Not overriding MarshalByRefObj.InitializeLifetimeService. // Per documentation, don't override Stream.ObjectInvariant. public override int Read(byte[] buffer, int offset, int count) { ThrowIfDisposed(); return InnerStream.Read(buffer, offset, count); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ThrowIfDisposed(); return InnerStream.ReadAsync(buffer, offset, count, cancellationToken); } public override int ReadByte() { ThrowIfDisposed(); return InnerStream.ReadByte(); } public override long Seek(long offset, SeekOrigin origin) { ThrowIfDisposed(); return InnerStream.Seek(offset, origin); } public override void SetLength(long value) { ThrowIfDisposed(); InnerStream.SetLength(value); } // Not overriding Object.ToString(). public override void Write(byte[] buffer, int offset, int count) { ThrowIfDisposed(); InnerStream.Write(buffer, offset, count); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ThrowIfDisposed(); return InnerStream.WriteAsync(buffer, offset, count, cancellationToken); } public override void WriteByte(byte value) { ThrowIfDisposed(); InnerStream.WriteByte(value); } protected void ThrowIfDisposed() { if (IsDisposed) { throw new ObjectDisposedException(null); } } } } ================================================ FILE: src/Common/PathHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; namespace System.Web { /// /// Helpers for working with IO paths. /// internal static class PathHelpers { /// /// Returns whether the path has the specified file extension. /// public static bool EndsWithExtension(string path, string extension) { Contract.Assert(path != null); Contract.Assert(extension != null && extension.Length > 0); if (path.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) { int extensionLength = extension.Length; int pathLength = path.Length; return (pathLength > extensionLength && path[pathLength - extensionLength - 1] == '.'); } return false; } } } ================================================ FILE: src/Common/PrefixContainer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace System.Web { /// /// This is a container for prefix values. It normalizes all the values into dotted-form and then stores /// them in a sorted array. All queries for prefixes are also normalized to dotted-form, and searches /// for ContainsPrefix are done with a binary search. /// internal class PrefixContainer { private readonly ICollection _originalValues; private readonly string[] _sortedValues; internal PrefixContainer(ICollection values) { if (values == null) { throw new ArgumentNullException("values"); } _originalValues = values; _sortedValues = _originalValues.ToArrayWithoutNulls(); Array.Sort(_sortedValues, StringComparer.OrdinalIgnoreCase); } internal bool ContainsPrefix(string prefix) { if (prefix == null) { throw new ArgumentNullException("prefix"); } if (prefix.Length == 0) { return _sortedValues.Length > 0; // only match empty string when we have some value } PrefixComparer prefixComparer = new PrefixComparer(prefix); bool containsPrefix = Array.BinarySearch(_sortedValues, prefix, prefixComparer) > -1; if (!containsPrefix) { // If there's something in the search boundary that starts with the same name // as the collection prefix that we're trying to find, the binary search would actually fail. // For example, let's say we have foo.a, foo.bE and foo.b[0]. Calling Array.BinarySearch // will fail to find foo.b because it will land on foo.bE, then look at foo.a and finally // failing to find the prefix which is actually present in the container (foo.b[0]). // Here we're doing another pass looking specifically for collection prefix. containsPrefix = Array.BinarySearch(_sortedValues, prefix + "[", prefixComparer) > -1; } return containsPrefix; } // Given "foo.bar", "foo.hello", "something.other", foo[abc].baz and asking for prefix "foo" will return: // - "bar"/"foo.bar" // - "hello"/"foo.hello" // - "abc"/"foo[abc]" internal IDictionary GetKeysFromPrefix(string prefix) { IDictionary result = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var entry in _originalValues) { if (entry != null) { if (entry.Length == prefix.Length) { // No key in this entry continue; } if (prefix.Length == 0) { GetKeyFromEmptyPrefix(entry, result); } else if (entry.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { GetKeyFromNonEmptyPrefix(prefix, entry, result); } } } return result; } private static void GetKeyFromEmptyPrefix(string entry, IDictionary results) { string key; int dotPosition = entry.IndexOf('.'); int bracketPosition = entry.IndexOf('['); int delimiterPosition = -1; if (dotPosition == -1) { if (bracketPosition != -1) { delimiterPosition = bracketPosition; } } else { if (bracketPosition == -1) { delimiterPosition = dotPosition; } else { delimiterPosition = Math.Min(dotPosition, bracketPosition); } } key = delimiterPosition == -1 ? entry : entry.Substring(0, delimiterPosition); results[key] = key; } private static void GetKeyFromNonEmptyPrefix(string prefix, string entry, IDictionary results) { string key = null; string fullName = null; int keyPosition = prefix.Length + 1; switch (entry[prefix.Length]) { case '.': int dotPosition = entry.IndexOf('.', keyPosition); if (dotPosition == -1) { dotPosition = entry.Length; } key = entry.Substring(keyPosition, dotPosition - keyPosition); fullName = entry.Substring(0, dotPosition); break; case '[': int bracketPosition = entry.IndexOf(']', keyPosition); if (bracketPosition == -1) { // Malformed for dictionary return; } key = entry.Substring(keyPosition, bracketPosition - keyPosition); fullName = entry.Substring(0, bracketPosition + 1); break; default: return; } if (!results.ContainsKey(key)) { results.Add(key, fullName); } } internal static bool IsPrefixMatch(string prefix, string testString) { if (testString == null) { return false; } if (prefix.Length == 0) { return true; // shortcut - non-null testString matches empty prefix } if (prefix.Length > testString.Length) { return false; // not long enough } if (!testString.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return false; // prefix doesn't match } if (testString.Length == prefix.Length) { return true; // exact match } // invariant: testString.Length > prefix.Length switch (testString[prefix.Length]) { case '.': case '[': return true; // known delimiters default: return false; // not known delimiter } } private class PrefixComparer : IComparer { private string _prefix; public PrefixComparer(string prefix) { _prefix = prefix; } public int Compare(string x, string y) { string testString = Object.ReferenceEquals(x, _prefix) ? y : x; if (IsPrefixMatch(_prefix, testString)) { return 0; } return StringComparer.OrdinalIgnoreCase.Compare(x, y); } } } } ================================================ FILE: src/Common/PropertyHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Reflection; #if ASPNETWEBAPI namespace System.Web.Http.Internal #else namespace System.Web.WebPages #endif { internal class PropertyHelper { private static ConcurrentDictionary _reflectionCache = new ConcurrentDictionary(); private Func _valueGetter; /// /// Initializes a fast property helper. This constructor does not cache the helper. /// [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "This is intended the Name is auto set differently per type and the type is internal")] public PropertyHelper(PropertyInfo property) { Contract.Assert(property != null); Name = property.Name; _valueGetter = MakeFastPropertyGetter(property); } /// /// Creates a single fast property setter. The result is not cached. /// /// propertyInfo to extract the getter for. /// a fast setter. /// This method is more memory efficient than a dynamically compiled lambda, and about the same speed. public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) where TDeclaringType : class { Contract.Assert(propertyInfo != null); MethodInfo setMethod = propertyInfo.GetSetMethod(); Contract.Assert(setMethod != null); Contract.Assert(!setMethod.IsStatic); Contract.Assert(setMethod.GetParameters().Length == 1); Contract.Assert(!propertyInfo.ReflectedType.IsValueType); // Instance methods in the CLR can be turned into static methods where the first parameter // is open over "this". This parameter is always passed by reference, so we have a code // path for value types and a code path for reference types. Type typeInput = propertyInfo.ReflectedType; Type typeValue = setMethod.GetParameters()[0].ParameterType; Delegate callPropertySetterDelegate; // Create a delegate TValue -> "TDeclaringType.Property" var propertySetterAsAction = setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, typeValue)); var callPropertySetterClosedGenericMethod = _callPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, typeValue); callPropertySetterDelegate = Delegate.CreateDelegate(typeof(Action), propertySetterAsAction, callPropertySetterClosedGenericMethod); return (Action)callPropertySetterDelegate; } public virtual string Name { get; protected set; } public object GetValue(object instance) { Contract.Assert(_valueGetter != null, "Must call Initialize before using this object"); return _valueGetter(instance); } /// /// Creates and caches fast property helpers that expose getters for every public get property on the underlying type. /// /// the instance to extract property accessors for. /// a cached array of all public property getters from the underlying type of this instance. public static PropertyHelper[] GetProperties(object instance) { return GetProperties(instance, CreateInstance, _reflectionCache); } /// /// Creates a single fast property getter. The result is not cached. /// /// propertyInfo to extract the getter for. /// a fast getter. /// This method is more memory efficient than a dynamically compiled lambda, and about the same speed. public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) { Contract.Assert(propertyInfo != null); MethodInfo getMethod = propertyInfo.GetGetMethod(); Contract.Assert(getMethod != null); Contract.Assert(!getMethod.IsStatic); Contract.Assert(getMethod.GetParameters().Length == 0); // Instance methods in the CLR can be turned into static methods where the first parameter // is open over "this". This parameter is always passed by reference, so we have a code // path for value types and a code path for reference types. Type typeInput = getMethod.ReflectedType; Type typeOutput = getMethod.ReturnType; Delegate callPropertyGetterDelegate; if (typeInput.IsValueType) { // Create a delegate (ref TDeclaringType) -> TValue Delegate propertyGetterAsFunc = getMethod.CreateDelegate(typeof(ByRefFunc<,>).MakeGenericType(typeInput, typeOutput)); MethodInfo callPropertyGetterClosedGenericMethod = _callPropertyGetterByReferenceOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput); callPropertyGetterDelegate = Delegate.CreateDelegate(typeof(Func), propertyGetterAsFunc, callPropertyGetterClosedGenericMethod); } else { // Create a delegate TDeclaringType -> TValue Delegate propertyGetterAsFunc = getMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(typeInput, typeOutput)); MethodInfo callPropertyGetterClosedGenericMethod = _callPropertyGetterOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput); callPropertyGetterDelegate = Delegate.CreateDelegate(typeof(Func), propertyGetterAsFunc, callPropertyGetterClosedGenericMethod); } return (Func)callPropertyGetterDelegate; } private static PropertyHelper CreateInstance(PropertyInfo property) { return new PropertyHelper(property); } // Implementation of the fast getter. private delegate TValue ByRefFunc(ref TDeclaringType arg); private static readonly MethodInfo _callPropertyGetterOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertyGetter", BindingFlags.NonPublic | BindingFlags.Static); private static readonly MethodInfo _callPropertyGetterByReferenceOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertyGetterByReference", BindingFlags.NonPublic | BindingFlags.Static); private static object CallPropertyGetter(Func getter, object @this) { return getter((TDeclaringType)@this); } private static object CallPropertyGetterByReference(ByRefFunc getter, object @this) { TDeclaringType unboxed = (TDeclaringType)@this; return getter(ref unboxed); } // Implementation of the fast setter. private static readonly MethodInfo _callPropertySetterOpenGenericMethod = typeof(PropertyHelper).GetMethod("CallPropertySetter", BindingFlags.NonPublic | BindingFlags.Static); private static void CallPropertySetter(Action setter, object @this, object value) { setter((TDeclaringType)@this, (TValue)value); } protected static PropertyHelper[] GetProperties(object instance, Func createPropertyHelper, ConcurrentDictionary cache) { // Using an array rather than IEnumerable, as this will be called on the hot path numerous times. PropertyHelper[] helpers; Type type = instance.GetType(); if (!cache.TryGetValue(type, out helpers)) { // We avoid loading indexed properties using the where statement. // Indexed properties are not useful (or valid) for grabbing properties off an anonymous object. IEnumerable properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(prop => prop.GetIndexParameters().Length == 0 && prop.GetMethod != null); var newHelpers = new List(); foreach (PropertyInfo property in properties) { PropertyHelper propertyHelper = createPropertyHelper(property); newHelpers.Add(propertyHelper); } helpers = newHelpers.ToArray(); cache.TryAdd(type, helpers); } return helpers; } } } ================================================ FILE: src/Common/Routing/Constraints/AlphaRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to contain only lowercase or uppercase letters A through Z in the English alphabet. /// public class AlphaRouteConstraint : RegexRouteConstraint { /// /// Initializes a new instance of the class. /// public AlphaRouteConstraint() : base(@"^[a-z]*$") { } } } ================================================ FILE: src/Common/Routing/Constraints/BoolRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only Boolean values. /// #if ASPNETWEBAPI public class BoolRouteConstraint : IHttpRouteConstraint #else public class BoolRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is bool) { return true; } bool result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Boolean.TryParse(valueString, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/CompoundRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route by several child constraints. /// #if ASPNETWEBAPI public class CompoundRouteConstraint : IHttpRouteConstraint #else public class CompoundRouteConstraint : IRouteConstraint #endif { /// /// Initializes a new instance of the class. /// /// The child constraints that must match for this constraint to match. #if ASPNETWEBAPI public CompoundRouteConstraint(IList constraints) #else public CompoundRouteConstraint(IList constraints) #endif { if (constraints == null) { throw Error.ArgumentNull("constraints"); } Constraints = constraints; } /// /// Gets the child constraints that must match for this constraint to match. /// #if ASPNETWEBAPI public IEnumerable Constraints { get; private set; } #else public IEnumerable Constraints { get; private set; } #endif /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { foreach (var constraint in Constraints) { #if ASPNETWEBAPI if (!constraint.Match(request, route, parameterName, values, routeDirection)) #else if (!constraint.Match(httpContext, route, parameterName, values, routeDirection)) #endif { return false; } } return true; } } } ================================================ FILE: src/Common/Routing/Constraints/DateTimeRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only values. /// #if ASPNETWEBAPI public class DateTimeRouteConstraint : IHttpRouteConstraint #else public class DateTimeRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is DateTime) { return true; } DateTime result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return DateTime.TryParse(valueString, CultureInfo.InvariantCulture, DateTimeStyles.None, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/DecimalRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only decimal values. /// #if ASPNETWEBAPI public class DecimalRouteConstraint : IHttpRouteConstraint #else public class DecimalRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is decimal) { return true; } decimal result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Decimal.TryParse(valueString, NumberStyles.Number, CultureInfo.InvariantCulture, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/DoubleRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only 64-bit floating-point values. /// #if ASPNETWEBAPI public class DoubleRouteConstraint : IHttpRouteConstraint #else public class DoubleRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is double) { return true; } double result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Double.TryParse(valueString, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/FloatRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Mvc; using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only 32-bit floating-point values. /// #if ASPNETWEBAPI public class FloatRouteConstraint : IHttpRouteConstraint #else public class FloatRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is float) { return true; } float result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Single.TryParse(valueString, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/GuidRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Mvc; using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only values. /// #if ASPNETWEBAPI public class GuidRouteConstraint : IHttpRouteConstraint #else public class GuidRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is Guid) { return true; } Guid result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Guid.TryParse(valueString, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/IntRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only 32-bit integer values. /// #if ASPNETWEBAPI public class IntRouteConstraint : IHttpRouteConstraint #else public class IntRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is int) { return true; } int result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Int32.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/LengthRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to be a string of a given length or within a given range of lengths. /// #if ASPNETWEBAPI public class LengthRouteConstraint : IHttpRouteConstraint #else public class LengthRouteConstraint : IRouteConstraint #endif { public LengthRouteConstraint(int length) { if (length < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("length", length, 0); } Length = length; } /// /// Initializes a new instance of the class that constrains /// a route parameter to be a string of a given length. /// /// The minimum length of the route parameter. /// The maximum length of the route parameter. public LengthRouteConstraint(int minLength, int maxLength) { if (minLength < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("minLength", minLength, 0); } if (maxLength < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxLength", maxLength, 0); } MinLength = minLength; MaxLength = maxLength; } /// /// Gets the length of the route parameter, if one is set. /// public int? Length { get; private set; } /// /// Gets the minimum length of the route parameter, if one is set. /// public int? MinLength { get; private set; } /// /// Gets the maximum length of the route parameter, if one is set. /// public int? MaxLength { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); int length = valueString.Length; if (Length.HasValue) { return length == Length.Value; } else { return length >= MinLength.Value && length <= MaxLength.Value; } } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/LongRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to represent only 64-bit integer values. /// #if ASPNETWEBAPI public class LongRouteConstraint : IHttpRouteConstraint #else public class LongRouteConstraint : IRouteConstraint #endif { /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { if (value is long) { return true; } long result; string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/MaxLengthRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to be a string with a maximum length. /// #if ASPNETWEBAPI public class MaxLengthRouteConstraint : IHttpRouteConstraint #else public class MaxLengthRouteConstraint : IRouteConstraint #endif { public MaxLengthRouteConstraint(int maxLength) { if (maxLength < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxLength", maxLength, 0); } MaxLength = maxLength; } /// /// Gets the maximum length of the route parameter. /// public int MaxLength { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return valueString.Length <= MaxLength; } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/MaxRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to be an integer with a maximum value. /// #if ASPNETWEBAPI public class MaxRouteConstraint : IHttpRouteConstraint #else public class MaxRouteConstraint : IRouteConstraint #endif { public MaxRouteConstraint(long max) { Max = max; } /// /// Gets the maximum value of the route parameter. /// public long Max { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { long longValue; if (value is long) { longValue = (long)value; return longValue <= Max; } string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) { return longValue <= Max; } } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/MinLengthRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to be a string with a maximum length. /// #if ASPNETWEBAPI public class MinLengthRouteConstraint : IHttpRouteConstraint #else public class MinLengthRouteConstraint : IRouteConstraint #endif { public MinLengthRouteConstraint(int minLength) { if (minLength < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("minLength", minLength, 0); } MinLength = minLength; } /// /// Gets the minimum length of the route parameter. /// public int MinLength { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return valueString.Length >= MinLength; } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/MinRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to be a long with a minimum value. /// #if ASPNETWEBAPI public class MinRouteConstraint : IHttpRouteConstraint #else public class MinRouteConstraint : IRouteConstraint #endif { public MinRouteConstraint(long min) { Min = min; } /// /// Gets the minimum value of the route parameter. /// public long Min { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { long longValue; if (value is long) { longValue = (long)value; return longValue >= Min; } string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) { return longValue >= Min; } } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/OptionalRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route by an inner constraint that doesn't fail when an optional parameter is set to its default value. /// #if ASPNETWEBAPI public class OptionalRouteConstraint : IHttpRouteConstraint #else public class OptionalRouteConstraint : IRouteConstraint #endif { /// /// Initializes a new instance of the class. /// /// The inner constraint to match if the parameter is not an optional parameter without a value #if ASPNETWEBAPI public OptionalRouteConstraint(IHttpRouteConstraint innerConstraint) #else public OptionalRouteConstraint(IRouteConstraint innerConstraint) #endif { if (innerConstraint == null) { throw Error.ArgumentNull("innerConstraint"); } InnerConstraint = innerConstraint; } /// /// Gets the inner constraint to match if the parameter is not an optional parameter without a value. /// #if ASPNETWEBAPI public IHttpRouteConstraint InnerConstraint { get; private set; } #else public IRouteConstraint InnerConstraint { get; private set; } #endif /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (route == null) { throw Error.ArgumentNull("route"); } if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } // If the parameter is optional and has no value, then pass the constraint object defaultValue; #if ASPNETWEBAPI var optionalParameter = RouteParameter.Optional; #else var optionalParameter = UrlParameter.Optional; #endif if (route.Defaults.TryGetValue(parameterName, out defaultValue) && defaultValue == optionalParameter) { object value; if (values.TryGetValue(parameterName, out value) && value == optionalParameter) { return true; } } #if ASPNETWEBAPI return InnerConstraint.Match(request, route, parameterName, values, routeDirection); #else return InnerConstraint.Match(httpContext, route, parameterName, values, routeDirection); #endif } } } ================================================ FILE: src/Common/Routing/Constraints/RangeRouteConstraintBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; #else using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constraints a route parameter to be an integer within a given range of values. /// #if ASPNETWEBAPI public class RangeRouteConstraint : IHttpRouteConstraint #else public class RangeRouteConstraint : IRouteConstraint #endif { public RangeRouteConstraint(long min, long max) { Min = min; Max = max; } /// /// Gets the minimum value of the route parameter. /// public long Min { get; private set; } /// /// Gets the maximum value of the route parameter. /// public long Max { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { long longValue; if (value is long) { longValue = (long)value; return longValue >= Min && longValue <= Max; } string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (Int64.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) { return longValue >= Min && longValue <= Max; } } return false; } } } ================================================ FILE: src/Common/Routing/Constraints/RegexRouteConstraint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; #if ASPNETWEBAPI using System.Net.Http; using System.Text.RegularExpressions; #else using System.Text.RegularExpressions; using System.Web.Routing; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing.Constraints #else namespace System.Web.Mvc.Routing.Constraints #endif { /// /// Constrains a route parameter to match a regular expression. /// #if ASPNETWEBAPI public class RegexRouteConstraint : IHttpRouteConstraint #else public class RegexRouteConstraint : IRouteConstraint #endif { private readonly Regex _regex; public RegexRouteConstraint(string pattern) { Pattern = pattern; _regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled); } /// /// Gets the regular expression pattern to match. /// public string Pattern { get; private set; } /// #if ASPNETWEBAPI public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) #else public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) #endif { if (parameterName == null) { throw Error.ArgumentNull("parameterName"); } if (values == null) { throw Error.ArgumentNull("values"); } object value; if (values.TryGetValue(parameterName, out value) && value != null) { string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); return _regex.IsMatch(valueString); } return false; } } } ================================================ FILE: src/Common/Routing/DefaultInlineConstraintResolver.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; #if ASPNETWEBAPI using System.Web.Http.Routing.Constraints; using ErrorResources = System.Web.Http.Properties.SRResources; #else using System.Web.Mvc.Routing.Constraints; using System.Web.Routing; using ErrorResources = System.Web.Mvc.Properties.MvcResources; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// /// The default implementation of . Resolves constraints by parsing /// a constraint key and constraint arguments, using a map to resolve the constraint type, and calling an /// appropriate constructor for the constraint type. /// public class DefaultInlineConstraintResolver : IInlineConstraintResolver { private readonly IDictionary _inlineConstraintMap = GetDefaultConstraintMap(); /// /// Gets the mutable dictionary that maps constraint keys to a particular constraint type. /// public IDictionary ConstraintMap { get { return _inlineConstraintMap; } } private static IDictionary GetDefaultConstraintMap() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { // Type-specific constraints { "bool", typeof(BoolRouteConstraint) }, { "datetime", typeof(DateTimeRouteConstraint) }, { "decimal", typeof(DecimalRouteConstraint) }, { "double", typeof(DoubleRouteConstraint) }, { "float", typeof(FloatRouteConstraint) }, { "guid", typeof(GuidRouteConstraint) }, { "int", typeof(IntRouteConstraint) }, { "long", typeof(LongRouteConstraint) }, // Length constraints { "minlength", typeof(MinLengthRouteConstraint) }, { "maxlength", typeof(MaxLengthRouteConstraint) }, { "length", typeof(LengthRouteConstraint) }, // Min/Max value constraints { "min", typeof(MinRouteConstraint) }, { "max", typeof(MaxRouteConstraint) }, { "range", typeof(RangeRouteConstraint) }, // Regex-based constraints { "alpha", typeof(AlphaRouteConstraint) }, { "regex", typeof(RegexRouteConstraint) } }; } /// #if ASPNETWEBAPI public virtual IHttpRouteConstraint ResolveConstraint(string inlineConstraint) #else public virtual IRouteConstraint ResolveConstraint(string inlineConstraint) #endif { if (inlineConstraint == null) { throw Error.ArgumentNull("inlineConstraint"); } string constraintKey; string argumentString; int indexOfFirstOpenParens = inlineConstraint.IndexOf('('); if (indexOfFirstOpenParens >= 0 && inlineConstraint.EndsWith(")", StringComparison.Ordinal)) { constraintKey = inlineConstraint.Substring(0, indexOfFirstOpenParens); argumentString = inlineConstraint.Substring(indexOfFirstOpenParens + 1, inlineConstraint.Length - indexOfFirstOpenParens - 2); } else { constraintKey = inlineConstraint; argumentString = null; } Type constraintType; if (!_inlineConstraintMap.TryGetValue(constraintKey, out constraintType)) { // Cannot resolve the constraint key return null; } #if ASPNETWEBAPI if (!typeof(IHttpRouteConstraint).IsAssignableFrom(constraintType)) #else if (!typeof(IRouteConstraint).IsAssignableFrom(constraintType)) #endif { throw Error.InvalidOperation(ErrorResources.DefaultInlineConstraintResolver_TypeNotConstraint, constraintType.Name, constraintKey); } #if ASPNETWEBAPI return (IHttpRouteConstraint)CreateConstraint(constraintType, argumentString); #else return (IRouteConstraint)CreateConstraint(constraintType, argumentString); #endif } private static object CreateConstraint(Type constraintType, string argumentString) { // No arguments - call the default constructor if (argumentString == null) { return Activator.CreateInstance(constraintType); } ConstructorInfo activationConstructor = null; object[] parameters = null; ConstructorInfo[] constructors = constraintType.GetConstructors(); // If there is only one constructor and it has a single parameter, pass the argument string directly // This is necessary for the Regex RouteConstraint to ensure that patterns are not split on commas. if (constructors.Length == 1 && constructors[0].GetParameters().Length == 1) { activationConstructor = constructors[0]; parameters = ConvertArguments(activationConstructor.GetParameters(), new string[] { argumentString }); } else { string[] arguments = argumentString.Split(',').Select(argument => argument.Trim()).ToArray(); ConstructorInfo[] matchingConstructors = constructors.Where(ci => ci.GetParameters().Length == arguments.Length).ToArray(); int constructorMatches = matchingConstructors.Length; if (constructorMatches == 0) { throw Error.InvalidOperation(ErrorResources.DefaultInlineConstraintResolver_CouldNotFindCtor, constraintType.Name, argumentString.Length); } else if (constructorMatches == 1) { activationConstructor = matchingConstructors[0]; parameters = ConvertArguments(activationConstructor.GetParameters(), arguments); } else { throw Error.InvalidOperation(ErrorResources.DefaultInlineConstraintResolver_AmbiguousCtors, constraintType.Name, argumentString.Length); } } return activationConstructor.Invoke(parameters); } private static object[] ConvertArguments(ParameterInfo[] parameterInfos, string[] arguments) { object[] parameters = new object[parameterInfos.Length]; for (int i = 0; i < parameterInfos.Length; i++) { ParameterInfo parameter = parameterInfos[i]; Type parameterType = parameter.ParameterType; parameters[i] = Convert.ChangeType(arguments[i], parameterType, CultureInfo.InvariantCulture); } return parameters; } } } ================================================ FILE: src/Common/Routing/DirectRouteBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; #if ASPNETWEBAPI using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Properties; #else using System.Web.Routing; #endif #if ASPNETWEBAPI using TActionDescriptor = System.Web.Http.Controllers.HttpActionDescriptor; using TParsedRoute = System.Web.Http.Routing.HttpParsedRoute; using TResources = System.Web.Http.Properties.SRResources; using TRoute = System.Web.Http.Routing.IHttpRoute; using TRouteDictionary = System.Collections.Generic.IDictionary; using TRouteDictionaryConcrete = System.Web.Http.Routing.HttpRouteValueDictionary; #else using TActionDescriptor = System.Web.Mvc.ActionDescriptor; using TParsedRoute = System.Web.Mvc.Routing.ParsedRoute; using TResources = System.Web.Mvc.Properties.MvcResources; using TRoute = System.Web.Routing.Route; using TRouteDictionary = System.Web.Routing.RouteValueDictionary; using TRouteDictionaryConcrete = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Represents a builder that creates direct routes to actions (attribute routes). internal class DirectRouteBuilder : IDirectRouteBuilder { private readonly TActionDescriptor[] _actions; private readonly bool _targetIsAction; private string _template; /// Initializes a new instance of the class. /// The action descriptors to which to create a route. /// /// A value indicating whether the route is configured at the action or controller level. /// public DirectRouteBuilder(IReadOnlyCollection actions, bool targetIsAction) { if (actions == null) { throw new ArgumentNullException("actions"); } _actions = actions.ToArray(); _targetIsAction = targetIsAction; } /// public string Name { get; set; } /// public string Template { get { return _template; } set { ParsedRoute = null; _template = value; } } /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] public TRouteDictionary Defaults { get; set; } /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] public TRouteDictionary Constraints { get; set; } /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] public TRouteDictionary DataTokens { get; set; } internal TParsedRoute ParsedRoute { get; set; } /// public int Order { get; set; } /// public decimal Precedence { get; set; } /// public IReadOnlyCollection Actions { get { return _actions; } } /// public bool TargetIsAction { get { return _targetIsAction; } } /// public virtual RouteEntry Build() { if (ParsedRoute == null) { ParsedRoute = RouteParser.Parse(Template); } ValidateParameters(ParsedRoute); TRouteDictionaryConcrete defaults; #if ASPNETWEBAPI defaults = Copy(Defaults); #else defaults = Copy(Defaults) ?? new RouteValueDictionary(); #endif TRouteDictionaryConcrete constraints = Copy(Constraints); TRouteDictionaryConcrete dataTokens = Copy(DataTokens) ?? new TRouteDictionaryConcrete(); dataTokens[RouteDataTokenKeys.Actions] = _actions; #if ASPNETWEBAPI if (!TargetIsAction) { dataTokens[RouteDataTokenKeys.Controller] = _actions[0].ControllerDescriptor; } #endif int order = Order; if (order != default(int)) { dataTokens[RouteDataTokenKeys.Order] = order; } decimal precedence = Precedence; if (precedence != default(decimal)) { dataTokens[RouteDataTokenKeys.Precedence] = precedence; } #if ASPNETWEBAPI if (constraints != null) { foreach (var constraint in constraints) { HttpRoute.ValidateConstraint(Template, constraint.Key, constraint.Value); } } HttpMessageHandler handler = null; IHttpRoute route = new HttpRoute(Template, defaults, constraints, dataTokens, handler, ParsedRoute); #else ControllerDescriptor controllerDescriptor = GetControllerDescriptor(); if (controllerDescriptor != null) { defaults["controller"] = controllerDescriptor.ControllerName; } if (TargetIsAction && _actions.Length == 1) { ActionDescriptor actionDescriptor = _actions[0]; defaults["action"] = actionDescriptor.ActionName; dataTokens[RouteDataTokenKeys.TargetIsAction] = true; } RouteAreaAttribute area = controllerDescriptor.GetAreaFrom(); string areaName = controllerDescriptor.GetAreaName(area); if (areaName != null) { dataTokens[RouteDataTokenKeys.Area] = areaName; dataTokens[RouteDataTokenKeys.UseNamespaceFallback] = false; Type controllerType = controllerDescriptor.ControllerType; if (controllerType != null) { dataTokens[RouteDataTokenKeys.Namespaces] = new[] { controllerType.Namespace }; } } Route route = new Route(Template, defaults, constraints, dataTokens, routeHandler: null); ConstraintValidation.Validate(route); #endif return new RouteEntry(Name, route); } // Accessible for tests internal virtual void ValidateParameters(TParsedRoute parsedRoute) { Contract.Assert(parsedRoute != null); if (parsedRoute.PathSegments != null) { foreach (var contentSegment in parsedRoute.PathSegments.OfType()) { if (contentSegment != null && contentSegment.Subsegments != null) { foreach (var parameterSegment in contentSegment.Subsegments.OfType()) { if (parameterSegment != null) { if (String.Equals(parameterSegment.ParameterName, "controller", StringComparison.OrdinalIgnoreCase)) { throw Error.InvalidOperation(TResources.DirectRoute_InvalidParameter_Controller); } else if (TargetIsAction && String.Equals(parameterSegment.ParameterName, "action", StringComparison.OrdinalIgnoreCase)) { throw Error.InvalidOperation(TResources.DirectRoute_InvalidParameter_Action); } } } } } } } internal static void ValidateRouteEntry(RouteEntry entry) { Contract.Assert(entry != null); TRoute route = entry.Route; Contract.Assert(route != null); TActionDescriptor[] targetActions = route.GetTargetActionDescriptors(); if (targetActions == null || targetActions.Length == 0) { throw new InvalidOperationException(TResources.DirectRoute_MissingActionDescriptors); } #if ASPNETWEBAPI if (route.Handler != null) { throw new InvalidOperationException(TResources.DirectRoute_HandlerNotSupported); } #else if (route.RouteHandler != null) { throw new InvalidOperationException(TResources.DirectRoute_RouteHandlerNotSupported); } #endif } private static TRouteDictionaryConcrete Copy(TRouteDictionary routeDictionary) { if (routeDictionary == null) { return null; } return new TRouteDictionaryConcrete(routeDictionary); } #if !ASPNETWEBAPI private ControllerDescriptor GetControllerDescriptor() { ControllerDescriptor controller = null; foreach (ActionDescriptor action in _actions) { if (controller == null) { controller = action.ControllerDescriptor; } else if (action.ControllerDescriptor != controller) { controller = null; break; } } return controller; } #endif } } ================================================ FILE: src/Common/Routing/DirectRouteFactoryContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; #if ASPNETWEBAPI using System.Web.Http.Controllers; using System.Web.Http.Properties; using TActionDescriptor = System.Web.Http.Controllers.HttpActionDescriptor; using TParsedRoute = System.Web.Http.Routing.HttpParsedRoute; using TRouteDictionary = System.Web.Http.Routing.HttpRouteValueDictionary; #else using System.Text; using System.Web.Mvc.Properties; using System.Web.Routing; using TActionDescriptor = System.Web.Mvc.ActionDescriptor; using TParsedRoute = System.Web.Mvc.Routing.ParsedRoute; using TRouteDictionary = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Represents a context that supports creating a direct route. public class DirectRouteFactoryContext { private readonly string _actionName; #if !ASPNETWEBAPI private readonly string _controllerName; #endif #if ASPNETWEBAPI private readonly string _prefix; #else private readonly string _areaPrefix; private readonly string _controllerPrefix; #endif private readonly IReadOnlyCollection _actions; private readonly IInlineConstraintResolver _inlineConstraintResolver; private readonly bool _targetIsAction; #if ASPNETWEBAPI /// Initializes a new instance of the /// The route prefix, if any, defined by the controller. /// The action descriptors to which to create a route. /// The inline constraint resolver. /// /// A value indicating whether the route is configured at the action or controller level. /// public DirectRouteFactoryContext(string prefix, IReadOnlyCollection actions, IInlineConstraintResolver inlineConstraintResolver, bool targetIsAction) #else /// Initializes a new instance of the /// The route prefix, if any, defined by the area. /// The route prefix, if any, defined by the controller. /// The action descriptors to which to create a route. /// The inline constraint resolver. /// /// A value indicating whether the route is configured at the action or controller level. /// public DirectRouteFactoryContext(string areaPrefix, string controllerPrefix, IReadOnlyCollection actions, IInlineConstraintResolver inlineConstraintResolver, bool targetIsAction) #endif { if (actions == null) { throw new ArgumentNullException("actions"); } if (inlineConstraintResolver == null) { throw new ArgumentNullException("inlineConstraintResolver"); } #if ASPNETWEBAPI _prefix = prefix; #else _areaPrefix = areaPrefix; _controllerPrefix = controllerPrefix; #endif _actions = actions; _inlineConstraintResolver = inlineConstraintResolver; TActionDescriptor firstDescriptor = actions.FirstOrDefault(); if (firstDescriptor != null) { _actionName = firstDescriptor.ActionName; #if !ASPNETWEBAPI ControllerDescriptor controllerDescriptor = firstDescriptor.ControllerDescriptor; if (controllerDescriptor != null) { _controllerName = controllerDescriptor.ControllerName; } #endif } _targetIsAction = targetIsAction; } #if ASPNETWEBAPI /// Gets the route prefix, if any, defined by the controller. public string Prefix { get { return _prefix; } } #else /// Gets the route prefix, if any, defined by the area. public string AreaPrefix { get { return _areaPrefix; } } /// Gets the route prefix, if any, defined by the controller. public string ControllerPrefix { get { return _controllerPrefix; } } #endif /// Gets the action descriptors to which to create a route. public IReadOnlyCollection Actions { get { return _actions; } } /// Gets the inline constraint resolver. public IInlineConstraintResolver InlineConstraintResolver { get { return _inlineConstraintResolver; } } /// /// Gets a value indicating whether the route is configured at the action or controller level. /// /// /// when the route is configured at the action level; otherwise /// (if the route is configured at the controller level). /// public bool TargetIsAction { get { return _targetIsAction; } } /// Creates a route builder that can build a route matching this context. /// The route template. /// A route builder that can build a route matching this context. public IDirectRouteBuilder CreateBuilder(string template) { return CreateBuilderInternal(template); } internal virtual IDirectRouteBuilder CreateBuilderInternal(string template) { return CreateBuilder(template, _inlineConstraintResolver); } /// Creates a route builder that can build a route matching this context. /// The route template. /// /// The inline constraint resolver to use, if any; otherwise, . /// /// A route builder that can build a route matching this context. public IDirectRouteBuilder CreateBuilder(string template, IInlineConstraintResolver constraintResolver) { DirectRouteBuilder builder = new DirectRouteBuilder(_actions, _targetIsAction); #if ASPNETWEBAPI string prefixedTemplate = BuildRouteTemplate(_prefix, template); #else string prefixedTemplate = BuildRouteTemplate(_areaPrefix, _controllerPrefix, template ?? String.Empty); #endif ValidateTemplate(prefixedTemplate); if (constraintResolver != null) { TRouteDictionary defaults = new TRouteDictionary(); TRouteDictionary constraints = new TRouteDictionary(); string detokenizedTemplate = InlineRouteTemplateParser.ParseRouteTemplate(prefixedTemplate, defaults, constraints, constraintResolver); TParsedRoute parsedRoute = RouteParser.Parse(detokenizedTemplate); decimal precedence = RoutePrecedence.Compute(parsedRoute, constraints); builder.Defaults = defaults; builder.Constraints = constraints; builder.Template = detokenizedTemplate; builder.Precedence = precedence; builder.ParsedRoute = parsedRoute; } else { builder.Template = prefixedTemplate; } return builder; } #if ASPNETWEBAPI private static string BuildRouteTemplate(string routePrefix, string routeTemplate) { if (String.IsNullOrEmpty(routeTemplate)) { return routePrefix ?? String.Empty; } // If the provider's template starts with '~/', ignore the route prefix if (routeTemplate.StartsWith("~/", StringComparison.Ordinal)) { return routeTemplate.Substring(2); } else if (String.IsNullOrEmpty(routePrefix)) { return routeTemplate; } else { // template and prefix both not null - combine them return routePrefix + '/' + routeTemplate; } } #else internal static string BuildRouteTemplate(string areaPrefix, string prefix, string template) { // If the attribute's template starts with '~/', ignore the area and controller prefixes if (template != null && template.StartsWith("~/", StringComparison.Ordinal)) { return template.Substring(2); } if (prefix == null && areaPrefix == null) { return template; } StringBuilder templateBuilder = new StringBuilder(); if (areaPrefix != null) { templateBuilder.Append(areaPrefix); } if (!String.IsNullOrEmpty(prefix)) { if (templateBuilder.Length > 0) { templateBuilder.Append('/'); } templateBuilder.Append(prefix); } if (!String.IsNullOrEmpty(template)) { if (templateBuilder.Length > 0) { templateBuilder.Append('/'); } templateBuilder.Append(template); } return templateBuilder.ToString(); } #endif private void ValidateTemplate(string template) { if (template != null && template.StartsWith("/", StringComparison.Ordinal)) { #if ASPNETWEBAPI string errorMessage = Error.Format(SRResources.AttributeRoutes_InvalidTemplate, template, _actionName); #else string errorMessage = Error.Format(MvcResources.RouteTemplate_CannotStart_WithForwardSlash, template, _actionName, _controllerName); #endif throw new InvalidOperationException(errorMessage); } } } } ================================================ FILE: src/Common/Routing/IDirectRouteBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; #if ASPNETWEBAPI using TActionDescriptor = System.Web.Http.Controllers.HttpActionDescriptor; using TRouteDictionary = System.Collections.Generic.IDictionary; #else using TActionDescriptor = System.Web.Mvc.ActionDescriptor; using TRouteDictionary = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Defines a builder that creates direct routes to actions (attribute routes). public interface IDirectRouteBuilder { /// Gets or sets the route name, if any; otherwise . string Name { get; set; } /// Gets or sets the route template. /// /// This value is the core route template that remains after resolving and removing any inline constraints. /// string Template { get; set; } /// Gets or sets the route defaults. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] TRouteDictionary Defaults { get; set; } /// Gets or sets the route constraints. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] TRouteDictionary Constraints { get; set; } /// Gets or sets the route data tokens. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Null and empty values are legitimate, separate options when constructing a route.")] TRouteDictionary DataTokens { get; set; } /// Gets or sets the route order. /// /// The route order disambiguates multiple matching routes and overrides precedence. /// The intended use of order is for an explicitly provided precedence override value. /// int Order { get; set; } /// Gets or sets the route precedence. /// /// The route order disambiguates multiple matching routes with the same order. /// The intended use of precedence is for default, automatically computed disambiguation based on inline /// constraint types. /// decimal Precedence { get; set; } /// Gets the action descriptors to which to create a route. IReadOnlyCollection Actions { get; } /// /// Gets a value indicating whether the route is configured at the action or controller level. /// /// /// when the route is configured at the action level; otherwise /// (if the route is configured at the controller level). /// bool TargetIsAction { get; } /// Creates a route entry based on the current property values. /// The route entry created. RouteEntry Build(); } } ================================================ FILE: src/Common/Routing/IDirectRouteFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// /// Defines a factory that creates a route directly to a set of action descriptors (an attribute route). /// public interface IDirectRouteFactory { /// Creates a direct route entry. /// The context to use to create the route. /// The direct route entry. RouteEntry CreateRoute(DirectRouteFactoryContext context); } } ================================================ FILE: src/Common/Routing/IDirectRouteProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Http.Controllers; namespace System.Web.Http.Routing { /// /// Defines a provider for routes that directly target action descriptors (attribute routes). /// public interface IDirectRouteProvider { /// Gets the direct routes for a controller. /// The controller descriptor. /// The action descriptors. /// The inline constraint resolver. /// A set of route entries for the controller. IReadOnlyList GetDirectRoutes( HttpControllerDescriptor controllerDescriptor, IReadOnlyList actionDescriptors, IInlineConstraintResolver constraintResolver); } } ================================================ FILE: src/Common/Routing/IInlineConstraintResolver.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI using TConstraint = System.Web.Http.Routing.IHttpRouteConstraint; #else using TConstraint = System.Web.Routing.IRouteConstraint; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// /// Defines an abstraction for resolving inline constraints as instances of . /// public interface IInlineConstraintResolver { /// /// Resolves the inline constraint. /// /// The inline constraint to resolve. /// The the inline constraint was resolved to. TConstraint ResolveConstraint(string inlineConstraint); } } ================================================ FILE: src/Common/Routing/IRoutePrefix.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Defines a route prefix. public interface IRoutePrefix { /// Gets the route prefix. string Prefix { get; } } } ================================================ FILE: src/Common/Routing/InlineRouteTemplateParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Text.RegularExpressions; #if ASPNETWEBAPI using System.Web.Http.Routing.Constraints; using ErrorResources = System.Web.Http.Properties.SRResources; #else using System.Web.Mvc.Routing.Constraints; using System.Web.Routing; using ErrorResources = System.Web.Mvc.Properties.MvcResources; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// /// This class is used by the AttributeRouting mapper, during configuration, /// to parse and strip defaults and constraints from the template. /// internal class InlineRouteTemplateParser { // One or more characters, matches "id" private const string ParameterNameRegex = @"(?.+?)"; // Zero or more inline constraints that start with a colon followed by zero or more characters // Optionally the constraint can have arguments within parentheses - necessary to capture characters like ":" and "}" // Matches ":int", ":length(2)", ":regex(\})", ":regex(:)" zero or more times private const string ConstraintRegex = @"(:(?.*?(\(.*?\))?))*"; // Optional "?" for optional parameters or a default value with an equal sign followed by zero or more characters // Matches "?", "=", "=abc" private const string DefaultValueRegex = @"(?\?|(=.*?))?"; private static readonly Regex _parameterRegex = new Regex( "{" + ParameterNameRegex + ConstraintRegex + DefaultValueRegex + "}", RegexOptions.Compiled); public static string ParseRouteTemplate(string routeTemplate, IDictionary defaults, IDictionary constraints, IInlineConstraintResolver constraintResolver) { Contract.Assert(defaults != null); Contract.Assert(constraints != null); MatchCollection parameterMatches = _parameterRegex.Matches(routeTemplate); foreach (Match parameterMatch in parameterMatches) { string parameterName = parameterMatch.Groups["parameterName"].Value; // We may need to strip out the initial wildcard used for wildcard parameters if (parameterName.StartsWith("*", StringComparison.OrdinalIgnoreCase)) { parameterName = parameterName.Substring(1); } // Add the default value if present Group defaultValueGroup = parameterMatch.Groups["defaultValue"]; object defaultValue = GetDefaultValue(defaultValueGroup); if (defaultValue != null) { defaults.Add(parameterName, defaultValue); } // Register inline constraints if present Group constraintGroup = parameterMatch.Groups["constraint"]; #if ASPNETWEBAPI bool isOptional = defaultValue == RouteParameter.Optional; #else bool isOptional = defaultValue == UrlParameter.Optional; #endif var constraint = GetInlineConstraint(constraintGroup, isOptional, constraintResolver); if (constraint != null) { constraints.Add(parameterName, constraint); } } // Replaces parameter matches with just the parameter name in braces // Strips out the optional '?', default value, inline constraints return _parameterRegex.Replace(routeTemplate, @"{${parameterName}}"); } private static object GetDefaultValue(Group defaultValueGroup) { if (defaultValueGroup.Success) { string defaultValueMatch = defaultValueGroup.Value; if (defaultValueMatch == "?") { #if ASPNETWEBAPI return RouteParameter.Optional; #else return UrlParameter.Optional; #endif } else { // Strip out the equal sign at the beginning Contract.Assert(defaultValueMatch.StartsWith("=", StringComparison.Ordinal)); return defaultValueMatch.Substring(1); } } return null; } #if ASPNETWEBAPI private static IHttpRouteConstraint GetInlineConstraint(Group constraintGroup, bool isOptional, IInlineConstraintResolver constraintResolver) #else private static IRouteConstraint GetInlineConstraint(Group constraintGroup, bool isOptional, IInlineConstraintResolver constraintResolver) #endif { #if ASPNETWEBAPI List parameterConstraints = new List(); #else List parameterConstraints = new List(); #endif foreach (Capture constraintCapture in constraintGroup.Captures) { string inlineConstraint = constraintCapture.Value; var constraint = constraintResolver.ResolveConstraint(inlineConstraint); if (constraint == null) { throw Error.InvalidOperation(ErrorResources.HttpRouteBuilder_CouldNotResolveConstraint, constraintResolver.GetType().Name, inlineConstraint); } parameterConstraints.Add(constraint); } if (parameterConstraints.Count > 0) { var constraint = parameterConstraints.Count == 1 ? parameterConstraints[0] : new CompoundRouteConstraint(parameterConstraints); if (isOptional) { // Constraints should match RouteParameter.Optional if the parameter is optional // This prevents contraining when there's no value specified constraint = new OptionalRouteConstraint(constraint); } return constraint; } return null; } } } ================================================ FILE: src/Common/Routing/PathContentSegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a segment of a URI that is not a separator. It contains subsegments such as literals and parameters. internal sealed class PathContentSegment : PathSegment { public PathContentSegment(List subsegments) { Subsegments = subsegments; } [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Not changing original algorithm.")] public bool IsCatchAll { get { // TODO: Verify this is correct. Maybe add an assert. // Performance sensitive // Caching count is faster for IList int subsegmentCount = Subsegments.Count; for (int i = 0; i < subsegmentCount; i++) { PathSubsegment seg = Subsegments[i]; PathParameterSubsegment paramterSubSegment = seg as PathParameterSubsegment; if (paramterSubSegment != null && paramterSubSegment.IsCatchAll) { return true; } } return false; } } public List Subsegments { get; private set; } #if ROUTE_DEBUGGING public override string LiteralText { get { List s = new List(); foreach (PathSubsegment subsegment in Subsegments) { s.Add(subsegment.LiteralText); } return String.Join(String.Empty, s.ToArray()); } } public override string ToString() { List s = new List(); foreach (PathSubsegment subsegment in Subsegments) { s.Add(subsegment.ToString()); } return "[ " + String.Join(", ", s.ToArray()) + " ]"; } #endif } } ================================================ FILE: src/Common/Routing/PathLiteralSubsegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a literal subsegment of a ContentPathSegment internal sealed class PathLiteralSubsegment : PathSubsegment { public PathLiteralSubsegment(string literal) { Literal = literal; } public string Literal { get; private set; } #if ROUTE_DEBUGGING public override string LiteralText { get { return Literal; } } public override string ToString() { return "\"" + Literal + "\""; } #endif } } ================================================ FILE: src/Common/Routing/PathParameterSubsegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a parameter subsegment of a ContentPathSegment internal sealed class PathParameterSubsegment : PathSubsegment { public PathParameterSubsegment(string parameterName) { if (parameterName.StartsWith("*", StringComparison.Ordinal)) { ParameterName = parameterName.Substring(1); IsCatchAll = true; } else { ParameterName = parameterName; } } public bool IsCatchAll { get; private set; } public string ParameterName { get; private set; } #if ROUTE_DEBUGGING public override string LiteralText { get { return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}"; } } public override string ToString() { return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}"; } #endif } } ================================================ FILE: src/Common/Routing/PathSegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a segment of a URI such as a separator or content internal abstract class PathSegment { #if ROUTE_DEBUGGING public abstract string LiteralText { get; } #endif } } ================================================ FILE: src/Common/Routing/PathSeparatorSegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a "/" separator in a URI internal sealed class PathSeparatorSegment : PathSegment { #if ROUTE_DEBUGGING public override string LiteralText { get { return "/"; } } public override string ToString() { return "\"/\""; } #endif } } ================================================ FILE: src/Common/Routing/PathSubsegment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // Represents a subsegment of a ContentPathSegment such as a parameter or a literal. internal abstract class PathSubsegment { #if ROUTE_DEBUGGING public abstract string LiteralText { get; } #endif } } ================================================ FILE: src/Common/Routing/RouteEntry.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI using TRoute = System.Web.Http.Routing.IHttpRoute; #else using TRoute = System.Web.Routing.Route; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Represents a named route. public class RouteEntry { private readonly string _name; private readonly TRoute _route; /// Initializes a new instance of the class. /// The route name, if any; otherwise, . /// The route. public RouteEntry(string name, TRoute route) { if (route == null) { throw new ArgumentNullException("route"); } _name = name; _route = route; } /// Gets the route name, if any; otherwise, . public string Name { get { return _name; } } /// Gets the route. public TRoute Route { get { return _route; } } } } ================================================ FILE: src/Common/Routing/RouteFactoryAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; #if ASPNETWEBAPI using TRouteDictionary = System.Collections.Generic.IDictionary; #else using TRouteDictionary = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Represents an attribute route that may contain custom constraints. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)] public abstract class RouteFactoryAttribute : Attribute, IDirectRouteFactory { private readonly string _template; /// Initializes a new instance of the class. /// The route template. protected RouteFactoryAttribute(string template) { _template = template; } /// Gets the route template. public string Template { get { return _template; } } /// Gets or sets the route name, if any; otherwise . public string Name { get; set; } /// Gets or sets the route order. public int Order { get; set; } /// Gets the route defaults, if any; otherwise . public virtual TRouteDictionary Defaults { get { return null; } } /// Gets the route constraints, if any; otherwise . public virtual TRouteDictionary Constraints { get { return null; } } /// Gets the route data tokens, if any; otherwise . public virtual TRouteDictionary DataTokens { get { return null; } } /// public RouteEntry CreateRoute(DirectRouteFactoryContext context) { if (context == null) { throw new ArgumentNullException("context"); } IDirectRouteBuilder builder = context.CreateBuilder(Template); Contract.Assert(builder != null); builder.Name = Name; builder.Order = Order; TRouteDictionary builderDefaults = builder.Defaults; if (builderDefaults == null) { builder.Defaults = Defaults; } else { TRouteDictionary defaults = Defaults; if (defaults != null) { foreach (KeyValuePair defaultItem in defaults) { builderDefaults[defaultItem.Key] = defaultItem.Value; } } } TRouteDictionary builderConstraints = builder.Constraints; if (builderConstraints == null) { builder.Constraints = Constraints; } else { TRouteDictionary constraints = Constraints; if (constraints != null) { foreach (KeyValuePair constraint in constraints) { builderConstraints[constraint.Key] = constraint.Value; } } } TRouteDictionary builderDataTokens = builder.DataTokens; if (builderDataTokens == null) { builder.DataTokens = DataTokens; } else { TRouteDictionary dataTokens = DataTokens; if (dataTokens != null) { foreach (KeyValuePair dataToken in dataTokens) { builderDataTokens[dataToken.Key] = dataToken.Value; } } } return builder.Build(); } } } ================================================ FILE: src/Common/Routing/RouteInfoDirectRouteFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; #if ASPNETWEBAPI using TRouteInfoProvider = System.Web.Http.Routing.IHttpRouteInfoProvider; #else using System.Web.Mvc.Properties; using TRouteInfoProvider = System.Web.Mvc.Routing.IRouteInfoProvider; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// /// This class is an adapter that turns an IHttpRouteInfoProvider into an IDirectRouteFactory. We need it because /// we already shipped IHttpRouteInfoProvider but want to standardize the internal implementation around the more /// general IDirectRouteFactory interface. /// We can remove this class if we ever stop supporting custom attributes that implement IHttpRouteInfoProvider. /// internal class RouteInfoDirectRouteFactory : IDirectRouteFactory { private readonly TRouteInfoProvider _infoProvider; public RouteInfoDirectRouteFactory(TRouteInfoProvider infoProvider) { if (infoProvider == null) { throw new ArgumentNullException("infoProvider"); } _infoProvider = infoProvider; } public RouteEntry CreateRoute(DirectRouteFactoryContext context) { Contract.Assert(context != null); IDirectRouteBuilder builder = context.CreateBuilder(_infoProvider.Template); Contract.Assert(builder != null); builder.Name = _infoProvider.Name; #if ASPNETWEBAPI builder.Order = _infoProvider.Order; #endif return builder.Build(); } } } ================================================ FILE: src/Common/Routing/RouteParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; #if ASPNETWEBAPI using ErrorResources = System.Web.Http.Properties.SRResources; using TParsedRoute = System.Web.Http.Routing.HttpParsedRoute; #else using ErrorResources = System.Web.Mvc.Properties.MvcResources; using TParsedRoute = System.Web.Mvc.Routing.ParsedRoute; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { // in the MVC case, route parsing is done for AttributeRouting's sake, so that // it could order the discovered routes before pushing them into the routeCollection, // where, unfortunately, they would be parsed again. internal static class RouteParser { private static string GetLiteral(string segmentLiteral) { // Scan for errant single { and } and convert double {{ to { and double }} to } // First we eliminate all escaped braces and then check if any other braces are remaining string newLiteral = segmentLiteral.Replace("{{", String.Empty).Replace("}}", String.Empty); if (newLiteral.Contains("{") || newLiteral.Contains("}")) { return null; } // If it's a valid format, we unescape the braces return segmentLiteral.Replace("{{", "{").Replace("}}", "}"); } private static int IndexOfFirstOpenParameter(string segment, int startIndex) { // Find the first unescaped open brace while (true) { startIndex = segment.IndexOf('{', startIndex); if (startIndex == -1) { // If there are no more open braces, stop return -1; } if ((startIndex + 1 == segment.Length) || ((startIndex + 1 < segment.Length) && (segment[startIndex + 1] != '{'))) { // If we found an open brace that is followed by a non-open brace, it's // a parameter delimiter. // It's also a delimiter if the open brace is the last character - though // it ends up being being called out as invalid later on. return startIndex; } // Increment by two since we want to skip both the open brace that // we're on as well as the subsequent character since we know for // sure that it is part of an escape sequence. startIndex += 2; } } internal static bool IsSeparator(string s) { return String.Equals(s, "/", StringComparison.Ordinal); } private static bool IsValidParameterName(string parameterName) { if (parameterName.Length == 0) { return false; } for (int i = 0; i < parameterName.Length; i++) { char c = parameterName[i]; if (c == '/' || c == '{' || c == '}') { return false; } } return true; } internal static bool IsInvalidRouteTemplate(string routeTemplate) { return routeTemplate.StartsWith("~", StringComparison.Ordinal) || routeTemplate.StartsWith("/", StringComparison.Ordinal) || (routeTemplate.IndexOf('?') != -1); } public static TParsedRoute Parse(string routeTemplate) { if (routeTemplate == null) { routeTemplate = String.Empty; } if (IsInvalidRouteTemplate(routeTemplate)) { throw Error.Argument("routeTemplate", ErrorResources.Route_InvalidRouteTemplate); } List uriParts = SplitUriToPathSegmentStrings(routeTemplate); Exception ex = ValidateUriParts(uriParts); if (ex != null) { throw ex; } List pathSegments = SplitUriToPathSegments(uriParts); Contract.Assert(uriParts.Count == pathSegments.Count, "The number of string segments should be the same as the number of path segments"); return new TParsedRoute(pathSegments); } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")] private static List ParseUriSegment(string segment, out Exception exception) { int startIndex = 0; List pathSubsegments = new List(); while (startIndex < segment.Length) { int nextParameterStart = IndexOfFirstOpenParameter(segment, startIndex); if (nextParameterStart == -1) { // If there are no more parameters in the segment, capture the remainder as a literal and stop string lastLiteralPart = GetLiteral(segment.Substring(startIndex)); if (lastLiteralPart == null) { exception = Error.Argument("routeTemplate", ErrorResources.Route_MismatchedParameter, segment); return null; } if (lastLiteralPart.Length > 0) { pathSubsegments.Add(new PathLiteralSubsegment(lastLiteralPart)); } break; } int nextParameterEnd = segment.IndexOf('}', nextParameterStart + 1); if (nextParameterEnd == -1) { exception = Error.Argument("routeTemplate", ErrorResources.Route_MismatchedParameter, segment); return null; } string literalPart = GetLiteral(segment.Substring(startIndex, nextParameterStart - startIndex)); if (literalPart == null) { exception = Error.Argument("routeTemplate", ErrorResources.Route_MismatchedParameter, segment); return null; } if (literalPart.Length > 0) { pathSubsegments.Add(new PathLiteralSubsegment(literalPart)); } string parameterName = segment.Substring(nextParameterStart + 1, nextParameterEnd - nextParameterStart - 1); pathSubsegments.Add(new PathParameterSubsegment(parameterName)); startIndex = nextParameterEnd + 1; } exception = null; return pathSubsegments; } private static List SplitUriToPathSegments(List uriParts) { List pathSegments = new List(); foreach (string pathSegment in uriParts) { bool isCurrentPartSeparator = IsSeparator(pathSegment); if (isCurrentPartSeparator) { pathSegments.Add(new PathSeparatorSegment()); } else { Exception exception; List subsegments = ParseUriSegment(pathSegment, out exception); Contract.Assert(exception == null, "This only gets called after the path has been validated, so there should never be an exception here"); pathSegments.Add(new PathContentSegment(subsegments)); } } return pathSegments; } internal static List SplitUriToPathSegmentStrings(string uri) { List parts = new List(); if (String.IsNullOrEmpty(uri)) { return parts; } int currentIndex = 0; // Split the incoming URI into individual parts while (currentIndex < uri.Length) { int indexOfNextSeparator = uri.IndexOf('/', currentIndex); if (indexOfNextSeparator == -1) { // If there are no more separators, the rest of the string is the last part string finalPart = uri.Substring(currentIndex); if (finalPart.Length > 0) { parts.Add(finalPart); } break; } string nextPart = uri.Substring(currentIndex, indexOfNextSeparator - currentIndex); if (nextPart.Length > 0) { parts.Add(nextPart); } Contract.Assert(uri[indexOfNextSeparator] == '/', "The separator char itself should always be a '/'."); parts.Add("/"); currentIndex = indexOfNextSeparator + 1; } return parts; } [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Not changing original algorithm")] [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")] private static Exception ValidateUriParts(List pathSegments) { Contract.Assert(pathSegments != null, "The value should always come from SplitUri(), and that function should never return null."); HashSet usedParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase); bool? isPreviousPartSeparator = null; bool foundCatchAllParameter = false; foreach (string pathSegment in pathSegments) { if (foundCatchAllParameter) { // If we ever start an iteration of the loop and we've already found a // catchall parameter then we have an invalid URI format. return Error.Argument("routeTemplate", ErrorResources.Route_CatchAllMustBeLast, "routeTemplate"); } bool isCurrentPartSeparator; if (isPreviousPartSeparator == null) { // Prime the loop with the first value isPreviousPartSeparator = IsSeparator(pathSegment); isCurrentPartSeparator = isPreviousPartSeparator.Value; } else { isCurrentPartSeparator = IsSeparator(pathSegment); // If both the previous part and the current part are separators, it's invalid if (isCurrentPartSeparator && isPreviousPartSeparator.Value) { return Error.Argument("routeTemplate", ErrorResources.Route_CannotHaveConsecutiveSeparators); } Contract.Assert(isCurrentPartSeparator != isPreviousPartSeparator.Value, "This assert should only happen if both the current and previous parts are non-separators. This should never happen because consecutive non-separators are always parsed as a single part."); isPreviousPartSeparator = isCurrentPartSeparator; } // If it's not a separator, parse the segment for parameters and validate it if (!isCurrentPartSeparator) { Exception exception; List subsegments = ParseUriSegment(pathSegment, out exception); if (exception != null) { return exception; } exception = ValidateUriSegment(subsegments, usedParameterNames); if (exception != null) { return exception; } foundCatchAllParameter = subsegments.Any(seg => (seg is PathParameterSubsegment) && ((PathParameterSubsegment)seg).IsCatchAll); } } return null; } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")] private static Exception ValidateUriSegment(List pathSubsegments, HashSet usedParameterNames) { bool segmentContainsCatchAll = false; Type previousSegmentType = null; foreach (PathSubsegment subsegment in pathSubsegments) { if (previousSegmentType != null) { if (previousSegmentType == subsegment.GetType()) { return Error.Argument("routeTemplate", ErrorResources.Route_CannotHaveConsecutiveParameters); } } previousSegmentType = subsegment.GetType(); PathLiteralSubsegment literalSubsegment = subsegment as PathLiteralSubsegment; if (literalSubsegment != null) { // Nothing to validate for literals - everything is valid } else { PathParameterSubsegment parameterSubsegment = subsegment as PathParameterSubsegment; if (parameterSubsegment != null) { string parameterName = parameterSubsegment.ParameterName; if (parameterSubsegment.IsCatchAll) { segmentContainsCatchAll = true; } // Check for valid characters in the parameter name if (!IsValidParameterName(parameterName)) { return Error.Argument("routeTemplate", ErrorResources.Route_InvalidParameterName, parameterName); } if (usedParameterNames.Contains(parameterName)) { return Error.Argument("routeTemplate", ErrorResources.Route_RepeatedParameter, parameterName); } else { usedParameterNames.Add(parameterName); } } else { Contract.Assert(false, "Invalid path subsegment type"); } } } if (segmentContainsCatchAll && (pathSubsegments.Count != 1)) { return Error.Argument("routeTemplate", ErrorResources.Route_CannotHaveCatchAllInMultiSegment); } return null; } } } ================================================ FILE: src/Common/Routing/RoutePrecedence.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; #if ASPNETWEBAPI using TParsedRoute = System.Web.Http.Routing.HttpParsedRoute; #else using TParsedRoute = System.Web.Mvc.Routing.ParsedRoute; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { internal static class RoutePrecedence { // Segments have the following order: // 1 - Literal segments // 2 - Constrained parameter segments / Multi-part segments // 3 - Unconstrained parameter segments // 4 - Constrained wildcard parameter segments // 5 - Unconstrained wildcard parameter segments internal static int ComputeDigit(PathContentSegment segment, IDictionary constraints) { if (segment.Subsegments.Count > 1) { // Multi-part segments should appear after literal segments but before parameter segments return 2; } PathSubsegment subsegment = segment.Subsegments[0]; // Literal segments always go first if (subsegment is PathLiteralSubsegment) { return 1; } else { PathParameterSubsegment parameterSegment = subsegment as PathParameterSubsegment; Contract.Assert(parameterSegment != null); int order = parameterSegment.IsCatchAll ? 5 : 3; // If there is a route constraint for the parameter, reduce order by 1 // Constrained parameters end up with order 2, Constrained catch alls end up with order 4 if (constraints != null && constraints.ContainsKey(parameterSegment.ParameterName)) { order--; } return order; } } public static decimal Compute(TParsedRoute parsedRoute, IDictionary constraints) { // Each precedence digit corresponds to one decimal place. For example, 3 segments with precedences 2, 1, // and 4 results in a combined precedence of 2.14 (decimal). IList segments = parsedRoute.PathSegments.OfType().ToArray(); decimal precedence = 0; uint divisor = 1; // The first digit occupies the one's place. for (int i = 0; i < segments.Count; i++) { PathContentSegment segment = segments[i]; int digit = ComputeDigit(segment, constraints); Contract.Assert(digit >= 0 && digit < 10); precedence = precedence + Decimal.Divide(digit, divisor); // The next digit occupies the subsequent place (always after the decimal point and growing to the // right). divisor *= 10; } return precedence; } } } ================================================ FILE: src/Common/Routing/SubRouteCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; #if ASPNETWEBAPI using System.Web.Http.Properties; using TRoute = System.Web.Http.Routing.IHttpRoute; using TRouteEntry = System.Web.Http.Routing.RouteEntry; #else using System.Web.Mvc.Properties; using TRoute = System.Web.Routing.Route; using TRouteEntry = System.Web.Mvc.Routing.RouteEntry; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { /// Represents a collection of route entries and the routes they contain. /// /// This is used in attribute routing, where we want to match multiple routes, and then later /// disambiguate which one is best. /// internal class SubRouteCollection : IReadOnlyCollection { private readonly List _routes = new List(); private readonly List _entries = new List(); public void Add(TRouteEntry entry) { Contract.Assert(entry != null); TRoute route = entry.Route; Contract.Assert(route != null); string name = entry.Name; if (name != null) { TRouteEntry duplicateEntry = _entries.SingleOrDefault((e) => e.Name == name); if (duplicateEntry != null) { ThrowExceptionForDuplicateRouteNames(name, route, duplicateEntry.Route); } } _routes.Add(route); _entries.Add(entry); } public void AddRange(IEnumerable entries) { foreach (RouteEntry entry in entries) { Add(entry); } } public int Count { get { return _entries.Count; } } public IEnumerator GetEnumerator() { return _routes.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_routes).GetEnumerator(); } public IReadOnlyCollection Entries { get { return _entries; } } private static void ThrowExceptionForDuplicateRouteNames(string name, TRoute route1, TRoute route2) { #if ASPNETWEBAPI throw new InvalidOperationException(String.Format( CultureInfo.CurrentCulture, SRResources.SubRouteCollection_DuplicateRouteName, name, route1.RouteTemplate, route2.RouteTemplate)); #else throw new InvalidOperationException(String.Format( CultureInfo.CurrentCulture, MvcResources.SubRouteCollection_DuplicateRouteName, name, route1.Url, route2.Url)); #endif } } } ================================================ FILE: src/Common/TaskHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Threading.Tasks { /// /// Helpers for safely using Task libraries. /// internal static class TaskHelpers { private static readonly Task _defaultCompleted = Task.FromResult(default(AsyncVoid)); private static readonly Task _completedTaskReturningNull = Task.FromResult(null); /// /// Returns a canceled Task. The task is completed, IsCanceled = True, IsFaulted = False. /// internal static Task Canceled() { return CancelCache.Canceled; } /// /// Returns a canceled Task of the given type. The task is completed, IsCanceled = True, IsFaulted = False. /// internal static Task Canceled() { return CancelCache.Canceled; } /// /// Returns a completed task that has no result. /// internal static Task Completed() { return _defaultCompleted; } /// /// Returns an error task. The task is Completed, IsCanceled = False, IsFaulted = True /// internal static Task FromError(Exception exception) { return FromError(exception); } /// /// Returns an error task of the given type. The task is Completed, IsCanceled = False, IsFaulted = True /// /// internal static Task FromError(Exception exception) { TaskCompletionSource tcs = new TaskCompletionSource(); tcs.SetException(exception); return tcs.Task; } internal static Task NullResult() { return _completedTaskReturningNull; } /// /// Used as the T in a "conversion" of a Task into a Task{T} /// private struct AsyncVoid { } /// /// This class is a convenient cache for per-type cancelled tasks /// private static class CancelCache { public static readonly Task Canceled = GetCancelledTask(); private static Task GetCancelledTask() { TaskCompletionSource tcs = new TaskCompletionSource(); tcs.SetCanceled(); return tcs.Task; } } } } ================================================ FILE: src/Common/TaskHelpersExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace System.Threading.Tasks { internal static class TaskHelpersExtensions { /// /// Cast Task to Task of object /// internal static async Task CastToObject(this Task task) { await task; return null; } /// /// Cast Task of T to Task of object /// internal static async Task CastToObject(this Task task) { return (object)await task; } /// /// Throws the first faulting exception for a task which is faulted. It preserves the original stack trace when /// throwing the exception. Note: It is the caller's responsibility not to pass incomplete tasks to this /// method, because it does degenerate into a call to the equivalent of .Wait() on the task when it hasn't yet /// completed. /// internal static void ThrowIfFaulted(this Task task) { task.GetAwaiter().GetResult(); } /// /// Attempts to get the result value for the given task. If the task ran to completion, then /// it will return true and set the result value; otherwise, it will return false. /// [SuppressMessage("Microsoft.Web.FxCop", "MW1201", Justification = "The usages here are deemed safe, and provide the implementations that this rule relies upon.")] internal static bool TryGetResult(this Task task, out TResult result) { if (task.Status == TaskStatus.RanToCompletion) { result = task.Result; return true; } result = default(TResult); return false; } } } ================================================ FILE: src/Common/TraceWriterExceptionMapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Net; using System.Net.Http; namespace System.Web.Http.Tracing { /// /// Extension methods for . /// internal static class TraceWriterExceptionMapper { private const string HttpErrorExceptionMessageFormat = "ExceptionMessage{0}='{1}'"; private const string HttpErrorExceptionTypeFormat = "ExceptionType{0}='{1}'"; private const string HttpErrorMessageDetailFormat = "MessageDetail='{0}'"; private const string HttpErrorModelStateErrorFormat = "ModelStateError=[{0}]"; private const string HttpErrorModelStatePairFormat = "{0}=[{1}]"; private const string HttpErrorStackTraceFormat = "StackTrace{0}={1}"; private const string HttpErrorUserMessageFormat = "UserMessage='{0}'"; /// /// Examines the given to determine whether it /// contains an and if so, modifies /// the to capture more detailed information. /// /// The to examine and modify. public static void TranslateHttpResponseException(TraceRecord traceRecord) { Contract.Assert(traceRecord != null); HttpResponseException httpResponseException = ExtractHttpResponseException(traceRecord.Exception); if (httpResponseException == null) { return; } HttpResponseMessage response = httpResponseException.Response; Contract.Assert(response != null); // If the status has been set already, do not overwrite it, // otherwise propagate the status into the record. if (traceRecord.Status == 0) { traceRecord.Status = response.StatusCode; } traceRecord.Level = GetMappedTraceLevel(httpResponseException) ?? traceRecord.Level; // HttpResponseExceptions often contain HttpError instances that carry // detailed information that may be filtered out by IncludeErrorDetailPolicy // before reaching the client. Capture it here for the trace. ObjectContent objectContent = response.Content as ObjectContent; if (objectContent == null) { return; } HttpError httpError = objectContent.Value as HttpError; if (httpError == null) { return; } object messageObject = null; object messageDetailsObject = null; List messages = new List(); if (httpError.TryGetValue(HttpErrorKeys.MessageKey, out messageObject)) { messages.Add(Error.Format(HttpErrorUserMessageFormat, messageObject)); } if (httpError.TryGetValue(HttpErrorKeys.MessageDetailKey, out messageDetailsObject)) { messages.Add(Error.Format(HttpErrorMessageDetailFormat, messageDetailsObject)); } // Extract the exception from this HttpError and then incrementally // walk down all inner exceptions. AddExceptions(httpError, messages); // ModelState errors are handled with a nested HttpError object modelStateErrorObject = null; if (httpError.TryGetValue(HttpErrorKeys.ModelStateKey, out modelStateErrorObject)) { HttpError modelStateError = modelStateErrorObject as HttpError; if (modelStateError != null) { messages.Add(FormatModelStateErrors(modelStateError)); } } traceRecord.Message = String.Join(", ", messages); } /// /// Gets the per the if the given exception is /// a ; otherwise . /// /// The exception. /// The corresponding trace level if the exception represents an . [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "shared source. called from a different assembly")] public static TraceLevel? GetMappedTraceLevel(Exception exception) { HttpResponseException httpResponseException = ExtractHttpResponseException(exception); if (httpResponseException == null) { return null; } return GetMappedTraceLevel(httpResponseException); } /// /// Gets the per the . /// /// The exception for which the trace level has to be found. /// The mapped trace level. public static TraceLevel? GetMappedTraceLevel(HttpResponseException httpResponseException) { Contract.Assert(httpResponseException != null); HttpResponseMessage response = httpResponseException.Response; Contract.Assert(response != null); TraceLevel? level = null; // Client level errors are downgraded to TraceLevel.Warn if ((int)response.StatusCode < (int)HttpStatusCode.InternalServerError) { level = TraceLevel.Warn; } // Non errors are downgraded to TraceLevel.Info if ((int)response.StatusCode < (int)HttpStatusCode.BadRequest) { level = TraceLevel.Info; } return level; } private static HttpResponseException ExtractHttpResponseException(Exception exception) { if (exception == null) { return null; } var httpResponseException = exception as HttpResponseException; if (httpResponseException != null) { return httpResponseException; } var aggregateException = exception as AggregateException; if (aggregateException != null) { httpResponseException = aggregateException .Flatten() .InnerExceptions .Select(ExtractHttpResponseException) .Where(ex => ex != null && ex.Response != null) .OrderByDescending(ex => ex.Response.StatusCode) .FirstOrDefault(); return httpResponseException; } return ExtractHttpResponseException(exception.InnerException); } /// /// Unpacks any exceptions in the given and adds /// them into a collection of name-value pairs that can be composed into a single string. /// /// /// This helper also iterates over all inner exceptions and unpacks them too. /// /// The to unpack. /// A collection of messages to which the new information should be added. private static void AddExceptions(HttpError httpError, List messages) { Contract.Assert(httpError != null); Contract.Assert(messages != null); object exceptionMessageObject = null; object exceptionTypeObject = null; object stackTraceObject = null; object innerExceptionObject = null; for (int i = 0; httpError != null; i++) { // For uniqueness, key names append the depth of inner exception string indexText = i == 0 ? String.Empty : Error.Format("[{0}]", i); if (httpError.TryGetValue(HttpErrorKeys.ExceptionTypeKey, out exceptionTypeObject)) { messages.Add(Error.Format(HttpErrorExceptionTypeFormat, indexText, exceptionTypeObject)); } if (httpError.TryGetValue(HttpErrorKeys.ExceptionMessageKey, out exceptionMessageObject)) { messages.Add(Error.Format(HttpErrorExceptionMessageFormat, indexText, exceptionMessageObject)); } if (httpError.TryGetValue(HttpErrorKeys.StackTraceKey, out stackTraceObject)) { messages.Add(Error.Format(HttpErrorStackTraceFormat, indexText, stackTraceObject)); } if (!httpError.TryGetValue(HttpErrorKeys.InnerExceptionKey, out innerExceptionObject)) { break; } Contract.Assert(!Object.ReferenceEquals(httpError, innerExceptionObject)); httpError = innerExceptionObject as HttpError; } } private static string FormatModelStateErrors(HttpError modelStateError) { Contract.Assert(modelStateError != null); List messages = new List(); foreach (var pair in modelStateError) { IEnumerable errorList = pair.Value as IEnumerable; if (errorList != null) { messages.Add(Error.Format(HttpErrorModelStatePairFormat, pair.Key, String.Join(", ", errorList))); } } return Error.Format(HttpErrorModelStateErrorFormat, String.Join(", ", messages)); } } } ================================================ FILE: src/Common/TypeExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; namespace System { /// /// Extension methods for . /// [EditorBrowsable(EditorBrowsableState.Never)] internal static class TypeExtensions { public static bool IsNullable(this Type type) { if (type.IsValueType) { // value types are only nullable if they are Nullable return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } else { // reference types are always nullable return true; } } } } ================================================ FILE: src/CommonAssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using System.Resources; using System.Runtime.InteropServices; #if !BUILD_GENERATED_VERSION [assembly: AssemblyCompany(".NET Foundation")] [assembly: AssemblyCopyright("Copyright © .NET Foundation. All rights reserved.")] #endif [assembly: AssemblyConfiguration("")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] #if !NOT_CLS_COMPLIANT [assembly: CLSCompliant(true)] #endif [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyMetadata("Serviceable", "True")] // =================================================================================== // TAKE CARE WHEN EDITING OR REMOVING ANYTHING BELOW THIS COMMENT. // BUILD_GENERATED_VERSION will be set in any CI build. Versions below are not used. // =================================================================================== #if (ASPNETMVC && (ASPNETWEBPAGES || ASPNETFACEBOOK || ASPNETHTTPFORMATTING)) || (ASPNETWEBPAGES && (ASPNETFACEBOOK || ASPNETHTTPFORMATTING)) || (ASPNETFACEBOOK && ASPNETHTTPFORMATTING) #error Runtime projects cannot define more than one of ASPNETMVC, ASPNETWEBPAGES, ASPNETFACEBOOK, or ASPNETHTTPFORMATTING #elif ASPNETHTTPFORMATTING #if !BUILD_GENERATED_VERSION [assembly: AssemblyVersion("6.0.0.0")] // ASPNETHTTPFORMATTING [assembly: AssemblyFileVersion("6.0.0.0")] // ASPNETHTTPFORMATTING #endif #elif ASPNETMVC #if !BUILD_GENERATED_VERSION [assembly: AssemblyVersion("5.3.0.0")] // ASPNETMVC [assembly: AssemblyFileVersion("5.3.0.0")] // ASPNETMVC #endif [assembly: AssemblyProduct("Microsoft ASP.NET MVC")] #elif ASPNETWEBPAGES #if !BUILD_GENERATED_VERSION [assembly: AssemblyVersion("3.0.0.0")] // ASPNETWEBPAGES [assembly: AssemblyFileVersion("3.0.0.0")] // ASPNETWEBPAGES #endif [assembly: AssemblyProduct("Microsoft ASP.NET Web Pages")] #elif ASPNETFACEBOOK #if !BUILD_GENERATED_VERSION [assembly: AssemblyVersion("1.1.0.0")] // ASPNETFACEBOOK [assembly: AssemblyFileVersion("1.1.0.0")] // ASPNETFACEBOOK #endif [assembly: AssemblyProduct("Microsoft ASP.NET Facebook")] #else #error Runtime projects must define ASPNETMVC, ASPNETWEBPAGES or ASPNETFACEBOOK #endif ================================================ FILE: src/CommonAssemblyInfo.vb ================================================ ' Copyright (c) .NET Foundation. All rights reserved. ' Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System Imports System.Reflection Imports System.Resources Imports System.Runtime.InteropServices ' =========================================================================== ' DO NOT EDIT OR REMOVE ANYTHING BELOW THIS COMMENT. ' Version numbers are automatically generated based on regular expressions. ' =========================================================================== 'ASPNETMVC 'ASPNETMVC ================================================ FILE: src/CommonResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.239 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.Internal.Web.Utils { using System; using System.Linq; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class CommonResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal CommonResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { // Find the CommonResources.resources file's full resource name in this assembly string commonResourcesName = global::System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames().Where(s => s.EndsWith("CommonResources.resources", StringComparison.OrdinalIgnoreCase)).Single(); // Trim off the ".resources" commonResourcesName = commonResourcesName.Substring(0, commonResourcesName.Length - 10); // Load the resource manager global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(commonResourcesName, typeof(CommonResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Value cannot be null or an empty string.. /// internal static string Argument_Cannot_Be_Null_Or_Empty { get { return ResourceManager.GetString("Argument_Cannot_Be_Null_Or_Empty", resourceCulture); } } /// /// Looks up a localized string similar to Value must be between {0} and {1}.. /// internal static string Argument_Must_Be_Between { get { return ResourceManager.GetString("Argument_Must_Be_Between", resourceCulture); } } /// /// Looks up a localized string similar to Value must be a value from the "{0}" enumeration.. /// internal static string Argument_Must_Be_Enum_Member { get { return ResourceManager.GetString("Argument_Must_Be_Enum_Member", resourceCulture); } } /// /// Looks up a localized string similar to Value must be greater than {0}.. /// internal static string Argument_Must_Be_GreaterThan { get { return ResourceManager.GetString("Argument_Must_Be_GreaterThan", resourceCulture); } } /// /// Looks up a localized string similar to Value must be greater than or equal to {0}.. /// internal static string Argument_Must_Be_GreaterThanOrEqualTo { get { return ResourceManager.GetString("Argument_Must_Be_GreaterThanOrEqualTo", resourceCulture); } } /// /// Looks up a localized string similar to Value must be less than {0}.. /// internal static string Argument_Must_Be_LessThan { get { return ResourceManager.GetString("Argument_Must_Be_LessThan", resourceCulture); } } /// /// Looks up a localized string similar to Value must be less than or equal to {0}.. /// internal static string Argument_Must_Be_LessThanOrEqualTo { get { return ResourceManager.GetString("Argument_Must_Be_LessThanOrEqualTo", resourceCulture); } } /// /// Looks up a localized string similar to Value cannot be an empty string. It must either be null or a non-empty string.. /// internal static string Argument_Must_Be_Null_Or_Non_Empty { get { return ResourceManager.GetString("Argument_Must_Be_Null_Or_Non_Empty", resourceCulture); } } } } ================================================ FILE: src/CommonResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Value cannot be null or an empty string. Value must be between {0} and {1}. Value must be a value from the "{0}" enumeration. Value must be greater than {0}. Value must be greater than or equal to {0}. Value must be less than {0}. Value must be less than or equal to {0}. Value cannot be an empty string. It must either be null or a non-empty string. ================================================ FILE: src/Directory.Build.props ================================================ true false v4.5 ================================================ FILE: src/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Assembly is delay-signed")] ================================================ FILE: src/Microsoft.AspNet.Facebook/Authorization/FacebookAuthorizeFilter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Web; using System.Web.Mvc; using Facebook; using Microsoft.AspNet.Facebook.Client; namespace Microsoft.AspNet.Facebook.Authorization { /// /// Authorization filter that verifies the signed requests and permissions from Facebook. /// public class FacebookAuthorizeFilter : IAuthorizationFilter { private static readonly Uri FacebookUri = new Uri("https://www.facebook.com/"); private static readonly Uri DefaultAuthorizationRedirectUrl = FacebookUri; private static readonly Uri DefaultCannotCreateCookiesRedirectPath = FacebookUri; // Facebook Missing Permissions, shortened version to not add excessively long query string parameters to the url. private const string MissingPermissionsQueryName = "__fb_mps"; private FacebookConfiguration _config; /// /// Initializes a new instance of the class. /// /// The . public FacebookAuthorizeFilter(FacebookConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } _config = config; } /// /// Called when authorization is required. /// /// The filter context. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Type references are needed for authorization")] public virtual void OnAuthorization(AuthorizationContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } IEnumerable authorizeAttributes = filterContext.ActionDescriptor .GetCustomAttributes(typeof(FacebookAuthorizeAttribute), inherit: true) .Union(filterContext.ActionDescriptor.ControllerDescriptor .GetCustomAttributes(typeof(FacebookAuthorizeAttribute), inherit: true)) .OfType(); if (!authorizeAttributes.Any()) { return; } FacebookClient client = _config.ClientProvider.CreateClient(); HttpRequestBase request = filterContext.HttpContext.Request; dynamic signedRequest = FacebookRequestHelpers.GetSignedRequest( filterContext.HttpContext, rawSignedRequest => { return client.ParseSignedRequest(rawSignedRequest); }); string userId = null; string accessToken = null; if (signedRequest != null) { userId = signedRequest.user_id; accessToken = signedRequest.oauth_token; } NameValueCollection parsedQueries = HttpUtility.ParseQueryString(request.Url.Query); HashSet requiredPermissions = PermissionHelper.GetRequiredPermissions(authorizeAttributes); bool handleError = !String.IsNullOrEmpty(parsedQueries["error"]); // This must occur AFTER the handleError calculation because it modifies the parsed queries. string redirectUrl = GetRedirectUrl(request, parsedQueries); // Check if there was an error and we should handle it. if (handleError) { Uri errorUrl; if (String.IsNullOrEmpty(_config.AuthorizationRedirectPath)) { errorUrl = DefaultAuthorizationRedirectUrl; } else { errorUrl = GetErroredAuthorizeUri(redirectUrl, requiredPermissions); } filterContext.Result = CreateRedirectResult(errorUrl); // There was an error so short circuit return; } FacebookContext facebookContext = new FacebookContext { Client = client, SignedRequest = signedRequest, AccessToken = accessToken, UserId = userId, Configuration = _config }; PermissionContext permissionContext = new PermissionContext { FacebookContext = facebookContext, FilterContext = filterContext, RequiredPermissions = requiredPermissions, }; // Check if we need to prompt for default permissions. if (signedRequest == null || String.IsNullOrEmpty(userId) || String.IsNullOrEmpty(accessToken)) { PromptDefaultPermissions(permissionContext, redirectUrl); } else if (requiredPermissions.Any()) { PermissionsStatus currentPermissionsStatus = _config.PermissionService.GetUserPermissionsStatus(userId, accessToken); // Instead of performing another request to gather "granted" permissions just parse the status IEnumerable currentPermissions = PermissionHelper.GetGrantedPermissions(currentPermissionsStatus); IEnumerable missingPermissions = requiredPermissions.Except(currentPermissions); // If we have missing permissions than we need to present a prompt or redirect to an error // page if there's an error. if (missingPermissions.Any()) { permissionContext.MissingPermissions = missingPermissions; permissionContext.DeclinedPermissions = PermissionHelper.GetDeclinedPermissions(currentPermissionsStatus); permissionContext.SkippedPermissions = PermissionHelper.GetSkippedPermissions( filterContext.HttpContext.Request, missingPermissions, permissionContext.DeclinedPermissions); // Add a query string parameter that enables us to identify if we've already prompted for missing permissions // and therefore can detect cookies. AddCookieVerificationQuery(parsedQueries); // Rebuild the redirect Url so it contains the new query string parameter. redirectUrl = GetRedirectUrl(request, parsedQueries); PromptMissingPermissions(permissionContext, redirectUrl); } } } /// /// Adds a query string parameter to a that enables a /// to detect when cookies are unavailable and then trigger the /// hook. /// /// List of query parameters that are used to create a url. protected static void AddCookieVerificationQuery(NameValueCollection queries) { // Add a query string parameter that enables us to identify if we've already prompted for missing permissions queries.Add(MissingPermissionsQueryName, "true"); } /// /// Called when authorization fails and need to create a redirect result. /// /// The redirect URL. /// The . public virtual JavaScriptRedirectResult CreateRedirectResult(Uri redirectUrl) { if (redirectUrl == null) { throw new ArgumentNullException("redirectUrl"); } return new JavaScriptRedirectResult(redirectUrl); } /// /// Returns an that indicates we want to show a permission prompt. Should only be used as a /// return value within the and methods. /// /// An that indicates that we want to show a permission prompt. protected ShowPromptResult ShowPrompt(PermissionContext context) { FacebookClient client = context.FacebookContext.Client; Uri navigationUrl = client.GetLoginUrl(context.RedirectUrl, _config.AppId, permissions: String.Join(",", context.RequiredPermissions)); return new ShowPromptResult(navigationUrl); } /// /// Invoked during after determining that cookies cannot be created. Default behavior /// redirects to a no cookies error page. /// /// Provides access to permission information associated with the user. protected virtual void OnCannotCreateCookies(PermissionContext context) { Uri redirectPath; if (String.IsNullOrEmpty(_config.CannotCreateCookieRedirectPath)) { redirectPath = DefaultCannotCreateCookiesRedirectPath; } else { redirectPath = GetCannotCreateCookiesUri(); } context.Result = CreateRedirectResult(redirectPath); } /// /// Invoked during when a prompt requests permissions that were skipped or revoked. /// Set the 's Result property to modify login flow. /// /// Provides access to permission information associated with the user. protected virtual void OnDeniedPermissionPrompt(PermissionContext context) { } /// /// Invoked during prior to a permission prompt that is requesting permissions that have /// not been requested before. Set the 's Result property to modify login flow. /// /// Provides access to permission information associated with the user. protected virtual void OnPermissionPrompt(PermissionContext context) { context.Result = ShowPrompt(context); } private Uri GetErroredAuthorizeUri(string originUrl, HashSet requiredPermissions) { if (requiredPermissions == null) { throw new ArgumentNullException("requiredPermissions"); } string requiredPermissionString = String.Join(",", requiredPermissions); UriBuilder authorizationUrlBuilder = new UriBuilder(new Uri(_config.AppUrl)); authorizationUrlBuilder.Path += _config.AuthorizationRedirectPath.Substring(1); authorizationUrlBuilder.Query = String.Format(CultureInfo.InvariantCulture, "originUrl={0}&permissions={1}", HttpUtility.UrlEncode(originUrl), HttpUtility.UrlEncode(requiredPermissionString)); return authorizationUrlBuilder.Uri; } private Uri GetCannotCreateCookiesUri() { UriBuilder noCookiesUrlBuilder = new UriBuilder(new Uri(_config.AppUrl)); noCookiesUrlBuilder.Path += _config.CannotCreateCookieRedirectPath.Substring(1); return noCookiesUrlBuilder.Uri; } private string GetRedirectUrl(HttpRequestBase request) { NameValueCollection queryNameValuePair = HttpUtility.ParseQueryString(request.Url.Query); return GetRedirectUrl(request, queryNameValuePair); } private string GetRedirectUrl(HttpRequestBase request, NameValueCollection queryNameValuePair) { // Don't propagate query strings added by Facebook OAuth Dialog queryNameValuePair.Remove("code"); queryNameValuePair.Remove("error"); queryNameValuePair.Remove("error_reason"); queryNameValuePair.Remove("error_description"); string redirectUrl = String.Format( CultureInfo.InvariantCulture, "{0}/{1}", _config.AppUrl.TrimEnd('/'), request.AppRelativeCurrentExecutionFilePath.TrimStart('~', '/')); string query = queryNameValuePair.ToString(); if (!String.IsNullOrEmpty(query)) { redirectUrl += "?" + query; } return redirectUrl; } private void PromptDefaultPermissions(PermissionContext permissionContext, string redirectUrl) { FacebookClient client = permissionContext.FacebookContext.Client; // Cannot obtain user information from signed_request, redirect to Facebook OAuth dialog. Uri navigationUrl = client.GetLoginUrl(redirectUrl, _config.AppId, permissions: null); permissionContext.FilterContext.Result = CreateRedirectResult(navigationUrl); } private void PromptMissingPermissions(PermissionContext permissionContext, string redirectUrl) { AuthorizationContext filterContext = permissionContext.FilterContext; HashSet requiredPermissions = permissionContext.RequiredPermissions; // If there were no errors it means that we will be prompted with a permission prompt. // Therefore, invoke the permission prompt hooks and navigate to the prompt. IEnumerable declinedPermissions = permissionContext.DeclinedPermissions; IEnumerable skippedPermissions = permissionContext.SkippedPermissions; IEnumerable missingPermissions = permissionContext.MissingPermissions; // Declined permissions and skipped permissions can persist through multiple pages. So we need to cross check // them against the current pages permissions, this will determine if we should invoke the denied permission hook. bool deniedPermissions = missingPermissions.Where( permission => declinedPermissions.Contains(permission) || skippedPermissions.Contains(permission)).Any(); permissionContext.RedirectUrl = redirectUrl; // See if our persisted permissions cookie doesn't exist and if we've had missing permissions before. // Essentially this checks to see if we've tried to persist permissions before and were unsuccessful due to an // inability to create cookies. if (!PermissionHelper.RequestedPermissionsCookieExists(filterContext.HttpContext.Request) && filterContext.HttpContext.Request.QueryString.Get(MissingPermissionsQueryName) != null) { OnCannotCreateCookies(permissionContext); } else { // The DeniedPermissionPromptHook will only be invoked if we detect there are denied permissions. // It is attempted instead of the permission hook to allow app creators to handle situations when a user // skip's or revokes previously prompted permissions. Ex: redirect to a different page. if (deniedPermissions) { OnDeniedPermissionPrompt(permissionContext); } else { OnPermissionPrompt(permissionContext); } // We persist the requested permissions in a cookie to know if a permission was denied in any way. // The persisted data allows us to detect skipping of permissions. PermissionHelper.PersistRequestedPermissions(filterContext, requiredPermissions); } filterContext.Result = permissionContext.Result; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Client/FacebookClientExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Facebook; namespace Microsoft.AspNet.Facebook.Client { /// /// Extension methods for . /// public static class FacebookClientExtensions { private const string PermissionsEndPoint = "me/permissions"; /// /// Gets the Facebook object located at a given path. /// /// The client. /// The object path. /// A Facebook object. public static Task GetFacebookObjectAsync(this FacebookClient client, string objectPath) { return GetFacebookObjectAsync(client, objectPath); } /// /// Gets the Facebook object located at a given path. /// /// The type of the Facebook object. /// The client. /// The object path. /// A Facebook object. public static Task GetFacebookObjectAsync(this FacebookClient client, string objectPath) where TFacebookObject : class { if (client == null) { throw new ArgumentNullException("client"); } if (objectPath == null) { throw new ArgumentNullException("objectPath"); } string path = objectPath + FacebookQueryHelper.GetFields(typeof(TFacebookObject)); return client.GetTaskAsync(path); } /// /// Gets the current user information. /// /// The client. /// The current user. public static Task GetCurrentUserAsync(this FacebookClient client) { return GetCurrentUserAsync(client); } /// /// Gets the current user information. /// /// The type of the user. /// The client. /// The current user. public static Task GetCurrentUserAsync(this FacebookClient client) where TUser : class { return GetFacebookObjectAsync(client, "me"); } /// /// Gets the current user friends information. /// /// The client. /// A collection of friends. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")] public static Task> GetCurrentUserFriendsAsync(this FacebookClient client) { return GetCurrentUserFriendsAsync(client); } /// /// Gets the current user friends information. /// /// The type of the user friend. /// The client. /// A collection of friends. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")] public static async Task> GetCurrentUserFriendsAsync(this FacebookClient client) where TUserFriend : class { FacebookGroupConnection friends = await GetFacebookObjectAsync>(client, "me/friends"); return friends != null ? friends.Data : new TUserFriend[0]; } /// /// Gets the permissions granted by the current user. /// /// The client. /// A collection of permissions. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")] public static async Task> GetCurrentUserPermissionsAsync(this FacebookClient client) { FacebookGroupConnection> permissionResults = await client.GetTaskAsync>>(PermissionsEndPoint); PermissionsStatus permissionsStatus = new PermissionsStatus(permissionResults.Data); return PermissionHelper.GetGrantedPermissions(permissionsStatus).ToList(); } /// /// Gets the current user statuses. /// /// The type of the status. /// The client. /// A collection of statuses. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")] public static async Task> GetCurrentUserStatusesAsync(this FacebookClient client) where TStatus : class { FacebookGroupConnection statuses = await GetFacebookObjectAsync>(client, "me/statuses"); return statuses != null ? statuses.Data : new TStatus[0]; } /// /// Gets the current user photos. /// /// The type of the photo. /// The client. /// A collection of user photos. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Using tasks")] public static async Task> GetCurrentUserPhotosAsync(this FacebookClient client) where TPhotos : class { FacebookGroupConnection photos = await GetFacebookObjectAsync>(client, "me/photos"); return photos != null ? photos.Data : new TPhotos[0]; } internal static Uri GetLoginUrl(this FacebookClient client, string redirectUrl, string appId, string permissions) { if (String.IsNullOrEmpty(redirectUrl)) { throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "redirectUrl"); } if (String.IsNullOrEmpty(appId)) { throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "appId"); } Dictionary loginUrlParameters = new Dictionary(); loginUrlParameters["redirect_uri"] = redirectUrl; loginUrlParameters["client_id"] = appId; if (!String.IsNullOrEmpty(permissions)) { loginUrlParameters["scope"] = permissions; } return client.GetLoginUrl(loginUrlParameters); } internal static IList> GetCurrentUserPermissionsStatus(this FacebookClient client) { FacebookGroupConnection> permissionResults = client.Get>>(PermissionsEndPoint); return permissionResults.Data; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Client/FacebookQueryHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Text; using Newtonsoft.Json; namespace Microsoft.AspNet.Facebook.Client { /// /// Helper for constructing Facebook Graph API queries. /// public static class FacebookQueryHelper { /// /// Gets the appropriate "fields" query parameter for the Facebook Graph API based on the public properties of the model type. /// /// Type of the model. /// The "fields" query parameter. public static string GetFields(Type modelType) { IList fieldNames = GetFieldNames(modelType, typesVisited: new HashSet()); if (fieldNames.Count > 0 && modelType != typeof(object)) { return "?fields=" + String.Join(",", fieldNames); } return String.Empty; } private static string GetConnectionFields(Type modelType, HashSet typesVisited) { if (typesVisited.Contains(modelType)) { throw new InvalidOperationException(Resources.CircularReferenceNotSupported); } IList fieldNames = GetFieldNames(modelType, typesVisited); if (fieldNames.Count > 0 && modelType != typeof(object)) { return ".fields(" + String.Join(",", fieldNames) + ")"; } return String.Empty; } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Lowercase is intended for field names.")] private static IList GetFieldNames(Type modelType, HashSet typesVisited) { Type connectionType; if (TryUnwrapConnectionType(modelType, out connectionType)) { modelType = connectionType; } PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); List facebookFields = new List(); foreach (PropertyInfo property in properties) { string propertyName = property.Name; AttributeCollection attributes = TypeDescriptor.GetProperties(modelType)[propertyName].Attributes; JsonIgnoreAttribute jsonIgnoreAttribute = (JsonIgnoreAttribute)attributes[typeof(JsonIgnoreAttribute)]; if (jsonIgnoreAttribute == null) { JsonPropertyAttribute jsonPropertyAttribute = (JsonPropertyAttribute)attributes[typeof(JsonPropertyAttribute)]; FacebookFieldModifierAttribute modifierAttribute = (FacebookFieldModifierAttribute)attributes[typeof(FacebookFieldModifierAttribute)]; StringBuilder fieldName = new StringBuilder( jsonPropertyAttribute != null ? jsonPropertyAttribute.PropertyName : propertyName); if (modifierAttribute != null) { fieldName.AppendFormat(CultureInfo.InvariantCulture, ".{0}", modifierAttribute.FieldModifier); } Type propertyType = property.PropertyType; if (TryUnwrapConnectionType(propertyType, out connectionType)) { typesVisited.Add(property.DeclaringType); fieldName.Append(GetConnectionFields(connectionType, typesVisited)); } facebookFields.Add(fieldName.ToString().ToLowerInvariant()); } } return facebookFields; } private static bool TryUnwrapConnectionType(Type modelType, out Type connectionType) { if (modelType.IsGenericType) { Type genericTypeDefinition = modelType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(FacebookConnection<>) || genericTypeDefinition == typeof(FacebookGroupConnection<>)) { Type genericArgumentType = modelType.GetGenericArguments()[0]; connectionType = genericArgumentType; return true; } } connectionType = null; return false; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Client/FacebookRequestHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web; namespace Microsoft.AspNet.Facebook.Client { internal static class FacebookRequestHelpers { private const string SignedRequestKey = "signed_request"; private const string ParsedSignedRequestKey = "parsed_signed_request"; public static dynamic GetSignedRequest(HttpContextBase context, Func parseSignedRequest) { if (context.Items.Contains(ParsedSignedRequestKey)) { return context.Items[ParsedSignedRequestKey]; } string rawSignedRequest = context.Request.Form[SignedRequestKey] ?? context.Request.QueryString[SignedRequestKey]; object signedRequest = null; if (!String.IsNullOrEmpty(rawSignedRequest)) { signedRequest = parseSignedRequest(rawSignedRequest); context.Items.Add(ParsedSignedRequestKey, signedRequest); } return signedRequest; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookAppSettingKeys.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook { internal static class FacebookAppSettingKeys { public static readonly string DisableAuthenticationModule = "Facebook:DisableAuthenticationModule"; public static readonly string AppId = "Facebook:AppId"; public static readonly string AppSecret = "Facebook:AppSecret"; public static readonly string AppNamespace = "Facebook:AppNamespace"; public static readonly string AppUrl = "Facebook:AppUrl"; public static readonly string AuthorizationRedirectPath = "Facebook:AuthorizationRedirectPath"; public static readonly string CannotCreateCookiesRedirectPath = "Facebook:CannotCreateCookiesRedirectPath"; } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookAuthenticationModule.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Security.Claims; using System.Threading; using System.Web; using Facebook; using Microsoft.AspNet.Facebook.Client; namespace Microsoft.AspNet.Facebook { internal sealed class FacebookAuthenticationModule : IHttpModule { public void Init(HttpApplication context) { context.AuthenticateRequest += (sender, e) => { HttpContext httpContext = ((HttpApplication)sender).Context; dynamic signedRequest = FacebookRequestHelpers.GetSignedRequest( new HttpContextWrapper(httpContext), rawSignedRequest => { FacebookConfiguration config = GlobalFacebookConfiguration.Configuration; FacebookClient client = config.ClientProvider.CreateClient(); return client.ParseSignedRequest(rawSignedRequest); }); if (signedRequest != null) { string userId = signedRequest.user_id; if (!String.IsNullOrEmpty(userId)) { ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, userId) })); Thread.CurrentPrincipal = principal; httpContext.User = principal; } } }; } public void Dispose() { } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookAuthorizeAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; namespace Microsoft.AspNet.Facebook { /// /// Restricts the access to requests with valid Facebook signed request parameter and to users that have the required permissions. /// This attribute can be declared on a controller, an action or both. /// [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "The attribute argument is already defined as a ReadOnlyCollection.")] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public sealed class FacebookAuthorizeAttribute : Attribute { private ReadOnlyCollection _permissions; /// /// Initializes a new instance of the class without requiring permissions. /// public FacebookAuthorizeAttribute() : this(new string[0]) { } /// /// Initializes a new instance of the class requiring permissions. /// /// The permissions. public FacebookAuthorizeAttribute(params string[] permissions) { if (permissions == null) { throw new ArgumentNullException("permissions"); } foreach (string permission in permissions) { if (permission.Contains(',')) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Resources.PermissionStringShouldNotContainComma, permission), "permissions"); } } _permissions = new ReadOnlyCollection(permissions); } /// /// Gets the required permissions. /// public ReadOnlyCollection Permissions { get { return _permissions; } } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookConfiguration.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.AspNet.Facebook.Providers; namespace Microsoft.AspNet.Facebook { /// /// Configuration for the Facebook application. /// public class FacebookConfiguration { private const string FacebookAppBaseUrl = "https://apps.facebook.com"; private readonly ConcurrentDictionary _properties = new ConcurrentDictionary(); private string _appUrl; private string _authorizationRedirectPath; private string _cannotCreateCookieRedirectPath; /// /// Gets or sets the App ID. /// public string AppId { get; set; } /// /// Gets or sets the App Secret. /// public string AppSecret { get; set; } /// /// Gets or sets the App Namespace. /// public string AppNamespace { get; set; } /// /// Gets or sets the URL path that the will /// redirect to when the user did not grant the required permissions. If value is not set it will result in a redirection /// to Facebook's home page. /// public string AuthorizationRedirectPath { get { return _authorizationRedirectPath; } set { EnsureRedirectPath(value, "AuthorizationRedirectPath"); _authorizationRedirectPath = value; } } /// /// Gets or sets the URL path that the will /// redirect to when the we determine that we are unable to create cookies. If value is not set it will result in a /// redirection to Facebook's home page. /// public string CannotCreateCookieRedirectPath { get { return _cannotCreateCookieRedirectPath; } set { EnsureRedirectPath(value, "CannotCreateCookieRedirectPath"); _cannotCreateCookieRedirectPath = value; } } /// /// Gets or sets the absolute URL for the Facebook App. /// [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "We prefer strings because this is read from appSettings")] public string AppUrl { get { if (String.IsNullOrEmpty(_appUrl)) { _appUrl = GetAppUrl(); } return _appUrl; } set { _appUrl = value; } } /// /// Gets or sets the . /// public IFacebookClientProvider ClientProvider { get; set; } /// /// Gets or sets the . /// public IFacebookPermissionService PermissionService { get; set; } /// /// Gets the additional properties associated with this instance. /// public ConcurrentDictionary Properties { get { return _properties; } } /// /// Loads the configuration properties from app settings. /// /// /// It will map the following keys from appSettings to the corresponding properties: /// Facebook:AppId = AppId, /// Facebook:AppSecret = AppSecret, /// Facebook:AppNamespace = AppNamespace, /// Facebook:AppUrl = AppUrl, /// Facebook:AuthorizationRedirectPath = AuthorizationRedirectPath. /// public virtual void LoadFromAppSettings() { AppId = ConfigurationManager.AppSettings[FacebookAppSettingKeys.AppId]; if (String.IsNullOrEmpty(AppId)) { throw new InvalidOperationException(String.Format( CultureInfo.CurrentCulture, Resources.AppSettingIsRequired, FacebookAppSettingKeys.AppId)); } AppSecret = ConfigurationManager.AppSettings[FacebookAppSettingKeys.AppSecret]; if (String.IsNullOrEmpty(AppSecret)) { throw new InvalidOperationException(String.Format( CultureInfo.CurrentCulture, Resources.AppSettingIsRequired, FacebookAppSettingKeys.AppSecret)); } AppNamespace = ConfigurationManager.AppSettings[FacebookAppSettingKeys.AppNamespace]; AppUrl = ConfigurationManager.AppSettings[FacebookAppSettingKeys.AppUrl]; AuthorizationRedirectPath = ConfigurationManager.AppSettings[FacebookAppSettingKeys.AuthorizationRedirectPath]; CannotCreateCookieRedirectPath = ConfigurationManager.AppSettings[FacebookAppSettingKeys.CannotCreateCookiesRedirectPath]; } private static void EnsureRedirectPath(string value, string redirectParameterName) { // Check for '~/' prefix while allowing null or empty value to be set. if (!String.IsNullOrEmpty(value) && !value.StartsWith("~/", StringComparison.Ordinal)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Resources.InvalidRedirectPath, redirectParameterName), "value"); } } private string GetAppUrl() { return String.Format( CultureInfo.InvariantCulture, "{0}/{1}", FacebookAppBaseUrl, String.IsNullOrEmpty(AppNamespace) ? AppId : AppNamespace); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookConnection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook { /// /// Model for the Facebook object connection. /// /// Type of the connection. public class FacebookConnection { /// /// Gets or sets the connection data. /// public T Data { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Facebook; using Microsoft.AspNet.Facebook.ModelBinders; namespace Microsoft.AspNet.Facebook { /// /// Provides access to Facebook-specific information. /// [FacebookContextBinderAttribute] public class FacebookContext { /// /// Gets or sets the parsed signed request. /// public dynamic SignedRequest { get; set; } /// /// Gets or sets the access token. /// public string AccessToken { get; set; } /// /// Gets or sets the user ID. /// public string UserId { get; set; } /// /// Gets or sets the Facebook client. /// public FacebookClient Client { get; set; } /// /// Gets or sets the . /// public FacebookConfiguration Configuration { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookFieldModifierAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.AspNet.Facebook { /// /// Allows adding field modifiers when querying Facebook Graph API. /// [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public sealed class FacebookFieldModifierAttribute : Attribute { /// /// Initializes a new instance of the class. /// /// The field modifier. public FacebookFieldModifierAttribute(string fieldModifier) { FieldModifier = fieldModifier; } /// /// Gets the field modifier. /// public string FieldModifier { get; private set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookGroupConnection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Microsoft.AspNet.Facebook { /// /// Model for the Facebook object connection when it contains a collection. /// /// Type of the collection element. public class FacebookGroupConnection { /// /// Gets or sets the connection data. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Needed for JSON deserialization")] public IList Data { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookHtmlHelperExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; namespace Microsoft.AspNet.Facebook { /// /// Facebook-related extension methods for . /// public static class FacebookHtmlHelperExtensions { /// /// Returns the signed_request in a hidden input element. /// /// The . /// The signed_request in a hidden input element. public static IHtmlString FacebookSignedRequest(this HtmlHelper htmlHelper) { string signedRequest = htmlHelper.ViewContext.HttpContext.Request.Params["signed_request"]; if (!String.IsNullOrEmpty(signedRequest)) { return htmlHelper.Hidden("signed_request", signedRequest); } return new HtmlString(String.Empty); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/FacebookRedirectContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using Microsoft.AspNet.Facebook.ModelBinders; namespace Microsoft.AspNet.Facebook { /// /// Provides access to the data redirected from . /// [FacebookRedirectContextBinder] public class FacebookRedirectContext { /// /// Gets or sets the origin URL. /// [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "We prefer strings because this is read from query strings")] public string OriginUrl { get; set; } /// /// Gets or sets the redirect URL. /// [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "We prefer strings because this is read from query strings")] public string RedirectUrl { get; set; } /// /// Gets or sets the required permissions specified on . /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] RequiredPermissions { get; set; } /// /// Gets or sets the . /// public FacebookConfiguration Configuration { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/GlobalFacebookConfiguration.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNet.Facebook.Providers; namespace Microsoft.AspNet.Facebook { /// /// Provides a global for ASP.NET applications. /// public static class GlobalFacebookConfiguration { private static readonly Lazy _configuration = new Lazy( () => { FacebookConfiguration config = new FacebookConfiguration(); config.ClientProvider = new DefaultFacebookClientProvider(config); config.PermissionService = new DefaultFacebookPermissionService(config); return config; }); /// /// Gets the global . /// public static FacebookConfiguration Configuration { get { return _configuration.Value; } } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.Models", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.ModelBinders", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.Realtime", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.Providers", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.Client", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.AspNet.Facebook.Authorization", Justification = "Namespace follows folder structure")] [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Assembly is delay-signed")] ================================================ FILE: src/Microsoft.AspNet.Facebook/JavaScriptRedirectResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook { /// /// Represents an that redirects to a permission prompt login via JavaScript. /// public class JavaScriptRedirectResult : ContentResult { internal Uri RedirectUrl { get; private set; } /// /// Creates a JavaScript based redirect . /// /// The url to redirect to. public JavaScriptRedirectResult(Uri redirectUrl) { RedirectUrl = redirectUrl; ContentType = "text/html"; Content = String.Format( CultureInfo.InvariantCulture, "", HttpUtility.JavaScriptStringEncode(RedirectUrl.AbsoluteUri)); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Microsoft.AspNet.Facebook.csproj ================================================  {821A136C-7C6F-44C6-A9E6-C39B5BFB1483} Library Properties Microsoft.AspNet.Facebook Microsoft.AspNet.Facebook $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETFACEBOOK TRACE;DEBUG;CODE_ANALYSIS;ASPNETFACEBOOK TRACE;ASPNETFACEBOOK TRACE;CODE_ANALYSIS;ASPNETFACEBOOK False ..\..\packages\Facebook.6.4.2\lib\net45\Facebook.dll ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll False False True True Resources.resx Properties\CommonAssemblyInfo.cs Designer ResXFileCodeGenerator Resources.Designer.cs {668e9021-ce84-49d9-98fb-df125a9fcdb0} System.Net.Http.Formatting {a0187bc2-8325-4bb2-8697-7f955cf4173e} System.Web.Http.WebHost {ddc1ce0c-486e-4e35-bb3b-eab61f8f9440} System.Web.Http {3d3ffd8a-624d-4e9b-954b-e1c105507975} System.Web.Mvc {8f18041b-9410-4c36-a9c5-067813df5f31} System.Web.Razor {22babb60-8f02-4027-affc-acf069954536} System.Web.WebPages.Deployment {0939b11a-fe4e-4ba1-8ad6-d97741ee314f} System.Web.WebPages.Razor {76efa9c5-8d7e-4fdf-b710-e20f8b6b00d2} System.Web.WebPages ================================================ FILE: src/Microsoft.AspNet.Facebook/ModelBinders/FacebookContextBinderAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook.ModelBinders { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] internal sealed class FacebookContextBinderAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new FacebookContextModelBinder(GlobalFacebookConfiguration.Configuration); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/ModelBinders/FacebookContextModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Facebook; using Microsoft.AspNet.Facebook.Client; namespace Microsoft.AspNet.Facebook.ModelBinders { /// /// Model binds an action method parameter to a . /// public class FacebookContextModelBinder : IModelBinder { private FacebookConfiguration _config; /// /// Initializes a new instance of the class. /// /// The . public FacebookContextModelBinder(FacebookConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } _config = config; } /// /// Binds the model to a value by using the specified controller context and binding context. /// /// The controller context. /// The binding context. /// /// The bound value. /// public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { FacebookClient client = _config.ClientProvider.CreateClient(); dynamic signedRequest = FacebookRequestHelpers.GetSignedRequest( controllerContext.HttpContext, rawSignedRequest => { return client.ParseSignedRequest(rawSignedRequest); }); if (signedRequest != null) { string accessToken = signedRequest.oauth_token; string userId = signedRequest.user_id; client.AccessToken = accessToken; return new FacebookContext { Client = client, SignedRequest = signedRequest, AccessToken = accessToken, UserId = userId, Configuration = _config }; } else { bindingContext.ModelState.AddModelError(bindingContext.ModelName, Resources.MissingSignedRequest); } return null; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/ModelBinders/FacebookRedirectContextBinderAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook.ModelBinders { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] internal sealed class FacebookRedirectContextBinderAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new FacebookRedirectContextModelBinder(GlobalFacebookConfiguration.Configuration); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/ModelBinders/FacebookRedirectContextModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web; using System.Web.Mvc; using Facebook; using Microsoft.AspNet.Facebook.Client; namespace Microsoft.AspNet.Facebook.ModelBinders { /// /// Model binds an action method parameter to a . /// public class FacebookRedirectContextModelBinder : IModelBinder { private FacebookConfiguration _config; /// /// Initializes a new instance of the class. /// /// The . public FacebookRedirectContextModelBinder(FacebookConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } _config = config; } /// /// Binds the model to a value by using the specified controller context and binding context. /// /// The controller context. /// The binding context. /// /// The bound value. /// public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HttpRequestBase request = controllerContext.HttpContext.Request; string originUrl = request.QueryString["originUrl"]; string permissions = request.QueryString["permissions"]; if (!String.IsNullOrEmpty(originUrl)) { if (!originUrl.StartsWith(_config.AppUrl, StringComparison.OrdinalIgnoreCase)) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format(CultureInfo.CurrentCulture, Resources.UrlCannotBeExternal, "originUrl", _config.AppUrl)); } } else { bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format(CultureInfo.CurrentCulture, Resources.ParameterIsRequired, "originUrl")); } string redirectUrl = null; string[] requiredPermissions = permissions != null ? permissions.Split(',') : new string[0]; if (bindingContext.ModelState.IsValid) { FacebookClient client = _config.ClientProvider.CreateClient(); // Don't want to redirect to a permissioned URL, the action authorize filters take care of that. redirectUrl = client.GetLoginUrl(originUrl, _config.AppId, String.Empty).AbsoluteUri; } return new FacebookRedirectContext { OriginUrl = originUrl, RequiredPermissions = requiredPermissions, RedirectUrl = redirectUrl, Configuration = _config }; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Models/ChangeEntry.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Newtonsoft.Json; namespace Microsoft.AspNet.Facebook.Models { /// /// Change entry from Facebook as part of Realtime Updates. /// public class ChangeEntry { /// /// Gets or sets the user id. /// [JsonProperty("UId")] public long UserId { get; set; } /// /// Gets or sets the changed fields. /// [JsonProperty("Changed_Fields")] public IEnumerable ChangedFields { get; set; } /// /// Gets or sets the time. /// public long Time { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Models/ChangeNotification.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.AspNet.Facebook.Models { /// /// Notification from Facebook as part of Realtime Updates. /// public class ChangeNotification { /// /// Gets or sets the object that has been updated. /// /// /// The Facebook object. /// public string Object { get; set; } /// /// Gets or sets the change entry. /// /// /// The change entry. /// public IEnumerable Entry { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Models/SubscriptionVerification.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Http; namespace Microsoft.AspNet.Facebook.Models { /// /// Subscription verification data from Facebook as part of Realtime Updates. /// [FromUri(Name = "hub")] public class SubscriptionVerification { /// /// Gets or sets the mode. /// /// /// The mode. /// public string Mode { get; set; } /// /// Gets or sets the verify_token. /// /// /// The verify_token. /// [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Justification = "This is a shipped API")] public string Verify_Token { get; set; } /// /// Gets or sets the challenge string. /// /// /// The challenge string. /// public string Challenge { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/PermissionContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook { /// /// Provides access to permission information associated with a user. /// public class PermissionContext { /// /// Permissions that were previously requested for but not granted for the lifetime of this application. This can happen /// by a user revoking, skipping or choosing not to allow permissions in the Facebook login dialog. /// /// /// This should only ever be "set" or modified within tests. /// public IEnumerable DeclinedPermissions { get; set; } /// /// Provides access to Facebook-specific information. /// /// /// This should only ever be "set" or modified within tests. /// public FacebookContext FacebookContext { get; set; } /// /// Provides access to filter information. /// /// /// This should only ever be "set" or modified within tests. /// public AuthorizationContext FilterContext { get; set; } /// /// The entire list of missing permissions for the current page, including and /// . /// /// /// This should only ever be "set" or modified within tests. /// public IEnumerable MissingPermissions { get; set; } /// /// The entire list of requested permissions for the current page. Includes permissions that were already prompted for. /// /// /// This should only ever be "set" or modified within tests. /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is public and can be set in tests.")] public HashSet RequiredPermissions { get; set; } /// /// The that should be used to control the login flow. If value is null then we will continue /// onto the action that is intended to be invoked. Non-null values short-circuit the action. /// public ActionResult Result { get; set; } /// /// Permissions that were previously requested for but skipped for the current page. This can happen from a user hitting /// the "skip" button when requesting permissions. /// /// Skips are tracked via cookies. If cookies are cleared skips will not be detected. /// /// This should only ever be "set" or modified within tests. /// public IEnumerable SkippedPermissions { get; set; } internal string RedirectUrl { get; set; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/PermissionHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook { internal static class PermissionHelper { // This cookie name is used to track permissions we've requested of the user to determine skipped permissions. public const string RequestedPermissionCookieName = "_fb_requested_permissions"; public static IEnumerable GetDeclinedPermissions(PermissionsStatus permissionsStatus) { return GetPermissionsWithStatus(permissionsStatus, PermissionStatus.Declined); } public static IEnumerable GetGrantedPermissions(PermissionsStatus permissionsStatus) { return GetPermissionsWithStatus(permissionsStatus, PermissionStatus.Granted); } public static IEnumerable GetPreviouslyRequestedPermissions(HttpRequestBase request) { HttpCookie existingCookie = request.Cookies.Get(RequestedPermissionCookieName); // If there's no cookie or an empty cookie then don't return a 1 element enumerable, return an empty one. if (existingCookie == null || String.IsNullOrEmpty(existingCookie.Value)) { return Enumerable.Empty(); } return existingCookie.Value.Split(','); } public static HashSet GetRequiredPermissions(IEnumerable facebookAuthorizeAttributes) { var requiredPermissions = new HashSet( facebookAuthorizeAttributes.SelectMany(attribute => attribute.Permissions), StringComparer.Ordinal); return requiredPermissions; } public static IEnumerable GetSkippedPermissions(HttpRequestBase request, IEnumerable missingPermissions, IEnumerable declinedPermissions) { IEnumerable previouslyRequestedPermissions = PermissionHelper.GetPreviouslyRequestedPermissions(request); IEnumerable previouslyRequestedMissingPermissions = missingPermissions.Where((permission) => previouslyRequestedPermissions.Contains(permission)); IEnumerable skippedPermissions = previouslyRequestedMissingPermissions.Except(declinedPermissions); return skippedPermissions; } public static void PersistRequestedPermissions(AuthorizationContext context, IEnumerable requestedPermissions) { HttpRequestBase request = context.HttpContext.Request; IEnumerable existingRequestedPermissions = GetPreviouslyRequestedPermissions(request); IEnumerable combinedRequestedPermissions = existingRequestedPermissions.Concat(requestedPermissions); // No need for duplicates combinedRequestedPermissions = combinedRequestedPermissions.Distinct(StringComparer.Ordinal); string newCookieValue = String.Join(",", combinedRequestedPermissions); HttpCookieCollection responseCookies = context.HttpContext.Response.Cookies; HttpCookie cookie = new HttpCookie(RequestedPermissionCookieName, newCookieValue); responseCookies.Add(cookie); } public static bool RequestedPermissionsCookieExists(HttpRequestBase request) { return request.Cookies.Get(RequestedPermissionCookieName) != null; } private static IEnumerable GetPermissionsWithStatus(PermissionsStatus permissionsStatus, PermissionStatus status) { return permissionsStatus.Status.Where(kvp => kvp.Value == status) .Select(kvp => kvp.Key); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/PermissionStatus.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook { /// /// Describes the kind of permission for an entry in the dictionary. /// public enum PermissionStatus { /// /// User granted permission. /// Granted, /// /// User declined permission. /// Declined, /// /// Unknown status of a permission. /// Unknown } } ================================================ FILE: src/Microsoft.AspNet.Facebook/PermissionsStatus.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Microsoft.AspNet.Facebook { /// /// Provides access to a users permissions status and the raw data returned from an API. /// public class PermissionsStatus { // Values representing permission statuses returned by the facebook graph API. private const string FacebookPermissionGranted = "granted"; private const string FacebookPermissionDeclined = "declined"; /// /// Constructs a more useable object than the provided api result. /// /// The raw data returned by the queried API. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This type is the raw type of data that Facebook returns to us.")] public PermissionsStatus(IList> apiResult) { ApiResult = apiResult; Status = ConvertApiResult(apiResult); } /// /// A parsed, easier to use version of the . /// public IDictionary Status { get; private set; } /// /// The raw data returned by the queried API. /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This type is the raw type of data that Facebook returns to us.")] public IList> ApiResult { get; private set; } /// /// Queries the member for the provided permission name. /// /// The name of the permission to query the for. /// The permission's status. public PermissionStatus this[string permission] { get { return Status[permission]; } } private static PermissionStatus ConvertPermissionStatus(string permissionStatus) { if (String.Equals(permissionStatus, FacebookPermissionGranted, StringComparison.OrdinalIgnoreCase)) { return PermissionStatus.Granted; } else if (String.Equals(permissionStatus, FacebookPermissionDeclined, StringComparison.OrdinalIgnoreCase)) { return PermissionStatus.Declined; } return PermissionStatus.Unknown; } private static IDictionary ConvertApiResult(IList> apiResults) { IDictionary transformedPermissions = new Dictionary(StringComparer.OrdinalIgnoreCase); if (apiResults != null && apiResults.Any()) { foreach (IDictionary permissionData in apiResults) { PermissionStatus status = ConvertPermissionStatus(permissionData["status"]); transformedPermissions.Add(permissionData["permission"], status); } } return transformedPermissions; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using Microsoft.AspNet.Facebook; [assembly: AssemblyTitle("Microsoft.AspNet.Facebook")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("Microsoft.AspNet.Facebook.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] ================================================ FILE: src/Microsoft.AspNet.Facebook/Providers/DefaultFacebookClientProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Facebook; using Newtonsoft.Json; namespace Microsoft.AspNet.Facebook.Providers { /// /// Default implementation of . /// public class DefaultFacebookClientProvider : IFacebookClientProvider { private FacebookConfiguration _config; /// /// Initializes a new instance of the class. /// /// The . public DefaultFacebookClientProvider(FacebookConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } _config = configuration; } /// /// Creates a with AppId and AppSecret that uses Json.NET for serialization and deserialization. /// Does not have an access token associated with it by default. /// /// The instance. public virtual FacebookClient CreateClient() { FacebookClient client = new FacebookClient(); client.AppId = _config.AppId; client.AppSecret = _config.AppSecret; client.SetJsonSerializers(JsonConvert.SerializeObject, JsonConvert.DeserializeObject); return client; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Providers/DefaultFacebookPermissionService.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Facebook; using Microsoft.AspNet.Facebook.Client; namespace Microsoft.AspNet.Facebook.Providers { /// /// Default implementation of . /// public class DefaultFacebookPermissionService : IFacebookPermissionService { private FacebookConfiguration _config; /// /// Initializes a new instance of the class. /// /// The . public DefaultFacebookPermissionService(FacebookConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } _config = configuration; } /// /// Gets the user permissions by calling the Facebook Graph API. /// /// The user id. /// The access token. /// The user permissions. public virtual IEnumerable GetUserPermissions(string userId, string accessToken) { if (userId == null) { throw new ArgumentNullException("userId"); } if (accessToken == null) { throw new ArgumentNullException("accessToken"); } PermissionsStatus permissionsStatus = GetUserPermissionsStatus(userId, accessToken); return PermissionHelper.GetGrantedPermissions(permissionsStatus); } /// /// Gets the users permission status by calling the Facebook graph API. /// /// The user id. /// The access token. /// The users permission status is in the following format { { "permissionName", "granted|declined" } }. public virtual PermissionsStatus GetUserPermissionsStatus(string userId, string accessToken) { if (userId == null) { throw new ArgumentNullException("userId"); } if (accessToken == null) { throw new ArgumentNullException("accessToken"); } FacebookClient client = _config.ClientProvider.CreateClient(); client.AccessToken = accessToken; IList> rawPermissionsStatus = client.GetCurrentUserPermissionsStatus(); PermissionsStatus permissionsStatus = new PermissionsStatus(rawPermissionsStatus); return permissionsStatus; } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Providers/IFacebookClientProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Facebook; namespace Microsoft.AspNet.Facebook.Providers { /// /// Provides an abstraction for creating . /// public interface IFacebookClientProvider { /// /// Creates an instance of . /// /// The instance. FacebookClient CreateClient(); } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Providers/IFacebookPermissionService.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.AspNet.Facebook.Providers { /// /// Provides an abstraction for retrieving the user permissions. /// public interface IFacebookPermissionService { /// /// Gets the user permissions. /// /// The user id. /// The access token. /// The user permissions. IEnumerable GetUserPermissions(string userId, string accessToken); /// /// Gets the users permission status. /// /// The user id. /// The access token. /// The user permissions status. PermissionsStatus GetUserPermissionsStatus(string userId, string accessToken); } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Realtime/FacebookRealtimeUpdateController.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using Facebook; using Microsoft.AspNet.Facebook.Models; namespace Microsoft.AspNet.Facebook.Realtime { /// /// for handling Facebook Realtime Update subscriptions. /// public abstract class FacebookRealtimeUpdateController : ApiController { private const string XHubSignatureHeaderName = "X-Hub-Signature"; private FacebookConfiguration _facebookConfiguration; /// /// Gets the verify token. /// /// /// The verify token. /// public abstract string VerifyToken { get; } /// /// Gets or sets the . /// /// /// The . /// public FacebookConfiguration FacebookConfiguration { get { if (_facebookConfiguration == null) { _facebookConfiguration = GlobalFacebookConfiguration.Configuration; } return _facebookConfiguration; } set { _facebookConfiguration = value; } } /// /// Handles the update. /// /// The notification. [NonAction] public abstract Task HandleUpdateAsync(ChangeNotification notification); /// /// Handles the HTTP GET requests from Facebook for subscription verification. /// /// The subscription verification. [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get", Justification = "Needs to be this name to follow routing conventions")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "HttpResponseMessage will be disposed by Web API")] public virtual HttpResponseMessage Get(SubscriptionVerification subscriptionVerification) { try { FacebookClient client = FacebookConfiguration.ClientProvider.CreateClient(); client.VerifyGetSubscription(subscriptionVerification.Mode, subscriptionVerification.Verify_Token, subscriptionVerification.Challenge, VerifyToken); } catch (ArgumentException argumentException) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, argumentException); } return new HttpResponseMessage { Content = new StringContent(subscriptionVerification.Challenge) }; } /// /// Handles the HTTP POST requests from Facebook for updates. /// public virtual async Task Post() { IEnumerable headerValues; if (Request.Headers.TryGetValues(XHubSignatureHeaderName, out headerValues)) { string signatureHeaderValue = headerValues.FirstOrDefault(); try { string contentString = await Request.Content.ReadAsStringAsync(); FacebookClient client = FacebookConfiguration.ClientProvider.CreateClient(); ChangeNotification notification = client.VerifyPostSubscription(signatureHeaderValue, contentString, typeof(ChangeNotification)) as ChangeNotification; await HandleUpdateAsync(notification); } catch (ArgumentException argumentException) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, argumentException); } catch (HttpResponseException responseException) { return responseException.Response; } } else { return Request.CreateErrorResponse( HttpStatusCode.BadRequest, String.Format(CultureInfo.CurrentCulture, Resources.MissingRequiredHeader, XHubSignatureHeaderName)); } return Request.CreateResponse(HttpStatusCode.OK); } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.35317 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.AspNet.Facebook { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Facebook.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to The app setting '{0}' is required and cannot be missing or empty. Make sure you set it in the configuration file.. /// internal static string AppSettingIsRequired { get { return ResourceManager.GetString("AppSettingIsRequired", resourceCulture); } } /// /// Looks up a localized string similar to This argument cannot be null or empty.. /// internal static string ArgumentCannotBeNullOrEmpty { get { return ResourceManager.GetString("ArgumentCannotBeNullOrEmpty", resourceCulture); } } /// /// Looks up a localized string similar to Circular type references are not supported.. /// internal static string CircularReferenceNotSupported { get { return ResourceManager.GetString("CircularReferenceNotSupported", resourceCulture); } } /// /// Looks up a localized string similar to Invalid '{0}'. The '{0}' can only be set relative to the AppUrl. Prefix the path with '~/'.. /// internal static string InvalidRedirectPath { get { return ResourceManager.GetString("InvalidRedirectPath", resourceCulture); } } /// /// Looks up a localized string similar to The required header '{0}' is missing.. /// internal static string MissingRequiredHeader { get { return ResourceManager.GetString("MissingRequiredHeader", resourceCulture); } } /// /// Looks up a localized string similar to The signed request is missing.. /// internal static string MissingSignedRequest { get { return ResourceManager.GetString("MissingSignedRequest", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' parameter is required.. /// internal static string ParameterIsRequired { get { return ResourceManager.GetString("ParameterIsRequired", resourceCulture); } } /// /// Looks up a localized string similar to The provided permission string '{0}' should not contain comma (,).. /// internal static string PermissionStringShouldNotContainComma { get { return ResourceManager.GetString("PermissionStringShouldNotContainComma", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' cannot be an external URL. It must have '{1}' as the prefix.. /// internal static string UrlCannotBeExternal { get { return ResourceManager.GetString("UrlCannotBeExternal", resourceCulture); } } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 The app setting '{0}' is required and cannot be missing or empty. Make sure you set it in the configuration file. This argument cannot be null or empty. Circular type references are not supported. Invalid '{0}'. The '{0}' can only be set relative to the AppUrl. Prefix the path with '~/'. The required header '{0}' is missing. The signed request is missing. The '{0}' parameter is required. The provided permission string '{0}' should not contain comma (,). The '{0}' cannot be an external URL. It must have '{1}' as the prefix. ================================================ FILE: src/Microsoft.AspNet.Facebook/ShowPromptResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.AspNet.Facebook { /// /// Represents an that redirects to a permission prompt login via JavaScript. /// public class ShowPromptResult : JavaScriptRedirectResult { /// /// Creates a JavaScript based redirect . /// /// The url that the prompt exists at. public ShowPromptResult(Uri promptUrl) : base(promptUrl) { } } } ================================================ FILE: src/Microsoft.AspNet.Facebook/packages.config ================================================  ================================================ FILE: src/Microsoft.Web.Helpers/Analytics.cshtml ================================================ @* Generator: WebPagesHelper *@ @helper GetGoogleHtml(string webPropertyId) { var webPropertyIdJson = new HtmlString(HttpUtility.JavaScriptStringEncode(webPropertyId, addDoubleQuotes: false)); } @helper GetGoogleAsyncHtml(string webPropertyId) { var webPropertyIdJson = new HtmlString(HttpUtility.JavaScriptStringEncode(webPropertyId, addDoubleQuotes: false)); } @helper GetYahooHtml(string account) { var accountJson = new HtmlString(HttpUtility.JavaScriptStringEncode(account, addDoubleQuotes: false)); } @helper GetStatCounterHtml(int project, string security) { var securityJson = new HtmlString(HttpUtility.JavaScriptStringEncode(security, addDoubleQuotes: false)); } ================================================ FILE: src/Microsoft.Web.Helpers/Facebook.cshtml ================================================ @* Generator: WebPagesHelper *@ @using System.Collections.Specialized @using System.Globalization @using System.Security @using System.Text @using System.Web.Helpers @using System.Web.WebPages.Scope @using WebMatrix.Data @using WebMatrix.WebData @functions { private const string FacebookCredentialsTableName = "webpages_FacebookCredentials"; private const string FacebookCredentialsIdColumn = "FacebookId"; private const string FacebookCredentialsUserIdColumn = "UserId"; private const string DefaultUserIdColumn = "UserId"; private const string DefaultUserNameColumn = "email"; private const string DefaultUserTableName = "UserProfile"; private const string DefaultFacebookPerms = "email"; private const string DefaultCallbackUrl = "~/Facebook/Login"; private const string FacebookApiProfileUrl = "https://graph.facebook.com/me"; private const string FacebookCookieAccessToken = "access_token"; private static readonly object _isInitializedKey = new object(); private static readonly object _membershipDBNameKey = new object(); private static readonly object _appIdKey = new object(); private static readonly object _appSecretKey = new object(); private static readonly object _language = new object(); public static bool HasMembershipIntegration { get { return !MembershipDBName.IsEmpty(); } } public static bool IsFacebookUserAuthenticated { get { return !GetFacebookCookieInfo(HttpContext, "uid").IsEmpty(); } } public static bool IsFacebookUserAssociated { get { return !GetAssociatedMembershipUserName().IsEmpty(); } } public static bool IsInitialized { get { return (bool)(ScopeStorage.CurrentScope[_isInitializedKey] ?? false); } private set { ScopeStorage.CurrentScope[_isInitializedKey] = value; } } public static string MembershipDBName { get { return (string)(ScopeStorage.CurrentScope[_membershipDBNameKey] ?? ""); } set { ScopeStorage.CurrentScope[_membershipDBNameKey] = value; } } public static string AppId { get { return (string)(ScopeStorage.CurrentScope[_appIdKey] ?? ""); } set { ScopeStorage.CurrentScope[_appIdKey] = value; } } public static string AppSecret { get { return (string)(ScopeStorage.CurrentScope[_appSecretKey] ?? ""); } set { ScopeStorage.CurrentScope[_appSecretKey] = value; } } public static string Language { get { return (string)(ScopeStorage.CurrentScope[_language] ?? "en_US"); } set { ScopeStorage.CurrentScope[_language] = value; } } private static HttpContextBase HttpContext { get { return new HttpContextWrapper(System.Web.HttpContext.Current); } } /// /// Initialize the helper with your Facebook application settings. /// /// If the 'membershipDBName' parameter is specified, Facebook membership integration will be enabled, /// allowing users to register and associate their Facebook user account (identified with the e-mail) /// with your site membership and the WebSecurity helper. /// In this case, the helper will initialize the WebSecurity WebMatrix helper automatically (if not done previously) /// and the store the Facebook membership information in the 'membershipDbName' database. /// ///Facebook application id. ///Facebook application secret. ///Name of the database used for storing the membership data. public static void Initialize(string appId, string appSecret, string membershipDbName = "") { AppId = appId; AppSecret = appSecret; IsInitialized = true; if (!membershipDbName.IsEmpty()) { MembershipDBName = membershipDbName; InitializeMembershipProviderIfNeeded(); InitializeFacebookTableIfNeeded(); } } /// /// Retrieves the Facebook profile of current logged in user. /// public static UserProfile GetFacebookUserProfile() { var accessToken = GetFacebookCookieInfo(HttpContext, FacebookCookieAccessToken); if (accessToken.IsEmpty()) { return null; } var userProfileUrl = new Uri(new UrlBuilder(FacebookApiProfileUrl).AddParam(FacebookCookieAccessToken, accessToken)); using (var client = new WebClient()) { using (var receiveStream = client.OpenRead(userProfileUrl)) { var result = new StreamReader(receiveStream).ReadToEnd(); var profile = Json.Decode(result); return profile; } } } /// /// Associates the specified User Name (e.g. email, depending on your membership model) with the current Facebook User Id from the logged user. /// ///The user name to associate the current logged-in facebook account to. public static void AssociateMembershipAccount(string userName) { if (!IsFacebookUserAuthenticated) { throw new InvalidOperationException("No Facebook user is authenticated."); } if (IsFacebookUserAssociated) { throw new InvalidOperationException("The authenticated Facebook user is already associated to a membership account."); } using (var db = Database.Open(MembershipDBName)) { var facebookUserId = GetFacebookCookieInfo(HttpContext, "uid").As(); var userId = WebSecurity.GetUserId(userName); db.Execute(String.Format(CultureInfo.InvariantCulture, "INSERT INTO {0} ({1}, {2}) VALUES (@0, @1)", FacebookCredentialsTableName, FacebookCredentialsUserIdColumn, FacebookCredentialsIdColumn), userId, facebookUserId); // User is registered in the application FormsAuthentication.SetAuthCookie(userName, false); } } public static bool MembershipLogin() { var user = GetAssociatedMembershipUserName(); if (!user.IsEmpty()) { // User is registered in the application FormsAuthentication.SetAuthCookie(user, false); return true; } else { return false; } } private static void InitializeMembershipProviderIfNeeded() { var provider = GetMembershipProvider(); if (IsMembershipProviderInitialized(provider)) { WebSecurity.InitializeDatabaseConnection(MembershipDBName, DefaultUserTableName, DefaultUserIdColumn, DefaultUserNameColumn, true); } } private static void InitializeFacebookTableIfNeeded() { using (var db = Database.Open(MembershipDBName)) { var table = db.QuerySingle("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @0", FacebookCredentialsTableName); if (table == null) { db.Execute(String.Format(CultureInfo.InvariantCulture, "CREATE TABLE {0} ({1} INT NOT NULL, {2} BIGINT NOT NULL)", FacebookCredentialsTableName, FacebookCredentialsUserIdColumn, FacebookCredentialsIdColumn)); } } } private static string GetAssociatedMembershipUserName() { var userName = ""; if (IsFacebookUserAuthenticated) { using (var db = Database.Open(MembershipDBName)) { var userId = db.QueryValue(String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM {1} WHERE {2} = LOWER(@0)", FacebookCredentialsUserIdColumn, FacebookCredentialsTableName, FacebookCredentialsIdColumn), GetFacebookCookieInfo(HttpContext, "uid")); if (userId != null) { userName = GetUserName(userId); } } } return userName; } private static string GetUserName(int userId) { var userName = ""; using (var db = Database.Open(MembershipDBName)) { var provider = GetMembershipProvider(); userName = db.QueryValue(String.Format(CultureInfo.InvariantCulture, "SELECT {0} FROM {1} WHERE {2} = @0", provider.UserNameColumn, provider.UserTableName, provider.UserIdColumn), userId); } return userName; } private static SimpleMembershipProvider GetMembershipProvider() { var provider = Membership.Provider as SimpleMembershipProvider; if (provider == null) { throw new InvalidOperationException("Simple Membership Provider not found."); } return provider; } private static bool IsMembershipProviderInitialized(SimpleMembershipProvider provider) { return provider.UserTableName.IsEmpty() || provider.UserIdColumn.IsEmpty() || provider.UserNameColumn.IsEmpty(); } internal static string GetFacebookCookieInfo(HttpContextBase httpContext, string key) { var request = httpContext.Request; var name = "fbs_" + AppId; if (request.Cookies[name] != null) { var value = request.Cookies[name].Value; var args = HttpUtility.ParseQueryString(value.Replace("\"", "")); if (!IsFacebookCookieValid(args)) { throw new InvalidOperationException("Invalid Facebook cookie."); } return args[key]; } else { return ""; } } private static bool IsFacebookCookieValid(NameValueCollection args) { var payload = new StringBuilder(); var keys = args.AllKeys; Array.Sort(keys); foreach (var key in keys) { if (!key.Equals("sig", StringComparison.OrdinalIgnoreCase)) { payload.AppendFormat("{0}={1}", key, args[key]); } } payload.Append(AppSecret); // Review: The HMAC uses MD5 which is not cryptographically secure. We need to investigate other Facebook authentication methods. var signature = new StringBuilder(); using (var md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create()) { var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(payload.ToString())); for (int i = 0; i < hash.Length; i++) { signature.Append(hash[i].ToString("X2", CultureInfo.InvariantCulture)); } } return String.Equals(args["sig"], signature.ToString(), StringComparison.OrdinalIgnoreCase); } public class UserProfile { public string Id { get; set; } public string Name { get; set; } public string First_Name { get; set; } public string Last_Name { get; set; } public string Link { get; set; } public string Bio { get; set; } public string Gender { get; set; } public string Email { get; set; } public string Timezone { get; set; } public string Locale { get; set; } public string Updated_Time { get; set; } } private static IHtmlString RawJS(string text) { return new HtmlString(HttpUtility.JavaScriptStringEncode(text)); } } @* Summary: Initialize the Facebook JavaScript SDK to be able to support the XFBML tags of the social plugins. *@ @helper GetInitializationScripts() {
} @* Summary: Shows a Facebook Login Button, with site membership integration, allowing users to login on your site with their Facebook account (e-mail). To use this method, you need to provide the 'membershipDbName' in the helper's Initialize() method. Parameter: registerUrl Specifies the URL to register the logged user, and associate it with a Membership account. Parameter: returnUrl Specifies URL to redirect after the user has successfully logged in (i.e. the Facebook User has a Membership Account). Parameter: callbackUrl Specifies the URL of the WebMatrix page that will handle the Membership login (default URL is ~/Facebook/Login.cshtml). Parameter: buttonText Specifies the Login Button Text. Parameter: autoLogoutLink When set to true, if the user is logged into Facebook the button will display a logout button instead. Parameter: size Specifies the button size: "small", "medium", "large", "xlarge". Paramter: length Specifies the text lenght: "long" --> 'Connect with Facebook" or "short" --> 'Connect'. Parameter: showFaces Specifies whether to display profile photos below the button. Parameter: extendedPermissions The extendedPermissions parameter can be used to request extended permissions from the user. For example, if you want to incorporate a user's photos into your application, set this value to “user_photos”. *@ @helper LoginButton( string registerUrl, string returnUrl = "~/", string callbackUrl = DefaultCallbackUrl, string buttonText = "", bool autoLogoutLink = false, string size = "medium", string length = "long", bool showFaces = false, string extendedPermissions = "") { var redirectUrl = new UrlBuilder(callbackUrl) .AddParam("registerUrl", new UrlBuilder(registerUrl)) .AddParam("returnUrl", new UrlBuilder(returnUrl)); var onLogin = String.Format(CultureInfo.InvariantCulture, "loginRedirect('{0}')", RawJS(redirectUrl)); extendedPermissions = extendedPermissions.IsEmpty() ? "email" : String.Concat("email,", extendedPermissions); @buttonText } @* Summary: Shows a Facebook Login Button, without integrating Facebook login with your site membership. Parameter: buttonText Specifies the Login Button Text. Parameter: autoLogoutLink When set to true, if the user is logged into Facebook the button will display a logout button instead. Parameter: size The button size: "small", "medium", "large", "xlarge". Parameter: length The text lenght: "long" --> 'Connect with Facebook" or "short" --> 'Connect' Parameter: onLogin Specifies the JavaScript action to execute after the user login. Parameter: showFaces Whether to display profile photos below the button. Parameter: extendedPermissions The extendedPermissions parameter can be used to request extended permissions from the user. For example, if you want to incorporate a user's photos into your application, set this value to “user_photos”. *@ @helper LoginButtonTagOnly( string buttonText = "", bool autoLogoutLink = false, string size = "long", string length = "short", string onLogin = "", bool showFaces = false, string extendedPermissions = "") { @buttonText } @* Summary: Shows a Facebook Like Button. When the user clicks the Like button on your site, a story appears in the user's friends' News Feed with a link back to your website. Parameter: href The URL to like. Parameter: buttonLayout The button layout: - standard: Displays social text to the right of the button and friends' profile photos below. Minimum width: 225 pixels. Default width: 450 pixels. Height: 35 pixels (without photos) or 80 pixels (with photos). - button_count: Displays the total number of likes to the right of the button. Minimum width: 90 pixels. Default width: 90 pixels. Height: 20 pixels. Parameter: showFaces Specifies whether to display profile photos below the button (standard layout only). Parameter: width The width of the Like button. Parameter: height The height of the plugin in pixels. Parameter: action The verb to display on the button: 'like', 'recommend'. Parameter: font The font to display in the button: 'arial', 'lucida grande', 'segoe ui', 'tahoma', 'trebuchet ms', 'verdana'. Parameter: colorScheme The color scheme for the like button: 'light' or 'dark'. Parameter: refLabel A label for tracking referrals; must be less than 50 characters and can contain alphanumeric characters and some punctuation (currently +/=-.:_). *@ @helper LikeButton( string href = "", string buttonLayout = "standard", bool showFaces = true, int width = 450, int height = 80, string action = "like", string font = "", string colorScheme = "light", string refLabel = "" ) { if (href.IsEmpty()) { href = Request.Url.OriginalString; } var src = new UrlBuilder("http://www.facebook.com/plugins/like.php") .AddParam("href", href) .AddParam("layout", buttonLayout) .AddParam("show_faces", showFaces) .AddParam("width", width) .AddParam("action", action) .AddParam("colorscheme", colorScheme) .AddParam("height", height) .AddParam("font", font) .AddParam("locale", Language) .AddParam("ref", refLabel); } @* Summary: Shows a Facebook Comments plugin. The Comments Box easily enables your users to comment on your site's content — whether it's for a web page, article, photo, or other piece of content. Then the user can share the comment on Facebook on their Wall and in their friends' streams. An 'Administer Comments' link will appear below the 'Post' button for developers of the application. Parameter: xid An id associated with the comments object (defaults to URL-encoded page URL). Parameter: width The width of the plugin, in pixels. Parameter: numPosts The number of comments to display, or 0 to hide all comments. Parameter: reverse Changes the order of comments and comment area to allow greater customization. Parameter: removeRoundedBox Removes the rounded box around the text area where comments are written to allow greater customization. *@ @helper Comments( string xid = "", int width = 550, int numPosts = 10, bool reverseOrder = false, bool removeRoundedBox = false) { xid="@xid" }numposts="@numPosts" width="@width" reverse="@reverseOrder" simple="@removeRoundedBox" > } @* Summary: Shows a Facebook Recommendations plugin. The Recommendations plugin shows personalized recommendations to your users. Since the content is hosted by Facebook, the plugin can display personalized recommendations whether or not the user has logged into your site. To generate the recommendations, the plugin considers all the social interactions with URLs from your site. For a logged in Facebook user, the plugin will give preference to and highlight objects her friends have interacted with. *NOTE*: This helper method requires that your site is published into a public address where others can use it. Check this tutorial on publishing with WebMatrix: http://www.asp.net/webmatrix/tutorials/publish-a-website Parameter: site The address of your published site. For example "www.fourthcoffee.com" Parameter: width The width of the plugin in pixels. Parameter: height The height of the plugin in pixels. Parameter: showHeader Whether to show a 'Recommendations' title bar. Parameter: colorScheme The color scheme of the plugin: 'light' or 'dark'. Parameter: font The font name for the plugin. Parameter: borderColor The border color of the plugin. Use common color names as Red, White, Black, and so on. Parameter: filter Allows you to filter which URLs are shown in the plugin. The plugin will only include URLs which contain the filter term in the first two path parameters of the URL. Parameter: refLabel A label for tracking referrals; must be less than 50 characters and can contain alphanumeric characters and some punctuation (currently +/=-.:_). *@ @helper Recommendations( string site = "", int width = 300, int height = 300, bool showHeader = true, string colorScheme = "light", string font = "", string borderColor = "", string filter = "", string refLabel = "" ) { if (site.IsEmpty()) { site = Request.Url.Host; } var src = new UrlBuilder("http://www.facebook.com/plugins/recommendations.php") .AddParam("site", site) .AddParam("width", width) .AddParam("height", height) .AddParam("header", showHeader) .AddParam("colorscheme", colorScheme) .AddParam("font", font) .AddParam("border_color", borderColor) .AddParam("filter", filter) .AddParam("ref", refLabel) .AddParam("locale", Language); } @* Summary: Shows a Facebook Like Box. The Like Box is a social plugin that enables Facebook Page owners to attract and gain Likes from their own website. The Like Box enables users to: - See how many users already like this page, and which of their friends like it too - Read recent posts from the page - Like the page with one click, without needing to visit the page Parameter: href The URL of the Facebook Page for this Like box. Parameter: width The width of the plugin in pixels. Parameter: height The height of the plugin in pixels. Parameter: colorScheme The color scheme of the plugin: 'light' or 'dark'. Parameter: connections Number of shown users who have liked this Page. Use 0 to avoid showing the users box. Parameter: showStream Shows the profile stream for the public profile of the page. Parameter: showHeader Shows the 'Find us on Facebook' bar at top. Only shown when either stream or connections are present. *@ @helper LikeBox( string href, int width = 292, int height = 587, string colorScheme = "light", int connections = 10, bool showStream = true, bool showHeader = true) { var src = new UrlBuilder("http://www.facebook.com/plugins/likebox.php") .AddParam("href", href) .AddParam("width", width) .AddParam("height", height) .AddParam("header", showHeader) .AddParam("colorscheme", colorScheme) .AddParam("connections", connections) .AddParam("stream", showStream) .AddParam("header", showHeader) .AddParam("locale", Language); } @* Summary: Shows a Facebook Facepile plugin. The Facepile plugin shows the Facebook profile pictures of the user's friends who have already signed up for your site. The plugin doesn't show up if the user is logged out of Facebook or doesn't have friends who have signed up for your site using Facebook. Parameter: maxRows The maximum number of rows of profile pictures to show. The plugin dynamically sizes its height; for example, if you specify a maximum of four rows, and there are only enough friends to fill two rows, the height of the plugin will be only what is needed for two rows of profile pictures. Parameter: width The width of the plugin in pixels. *@ @helper Facepile( int maxRows = 1, int width = 200) { } @* Summary: Shows a Facebook Live Stream plugin. The Live Stream plugin lets users visiting your site or application share activity and comments in real time. The Live Stream Box works best when you are running a real-time event, like live streaming video for concerts, speeches, or webcasts, live Web chats, webinars, massively multiplayer games. Parameter: width The width of the plugin in pixels. Parameter: height The height of the plugin in pixels. Parameter: xid If you have multiple live stream boxes on the same page, specify a unique 'xid' for each. Parameter: viaUrl The URL that users are redirected to when they click on your app name on a status (if not specified, your Connect URL is used). Parameter: alwaysPostToFriends If set, all user posts will always go to their profile. This option should only be used when users' posts are likely to make sense outside of the context of the event. *@ @helper LiveStream( int width = 400, int height = 500, string xid = "", string viaUrl = "", bool alwaysPostToFriends = false) { var builder = new UrlBuilder("http://www.facebook.com/plugins/live_stream_box.php") .AddParam("app_id", AppId) .AddParam("width", width) .AddParam("height", height) .AddParam("always_post_to_friends", alwaysPostToFriends) .AddParam("locale", Language); if (!xid.IsEmpty()) { builder.AddParam("xid", xid); builder.AddParam("via_url", viaUrl); } } @* Summary: Shows a Facebook Activity Feed plugin. The Activity Feed plugin displays the most interesting recent activity taking place on your site. Since the content is hosted by Facebook, the plugin can display personalized content whether or not the user has logged into your site. The activity feed displays stories both when users like content on your site and when users share content from your site back to Facebook. *NOTE*: This helper method requires that your site is published into a public address where others can use it. Check this tutorial on publishing with WebMatrix: http://www.asp.net/webmatrix/tutorials/publish-a-website Parameter: site The address of your published site. For example "www.fourthcoffee.com" Parameter: width The width of the plugin in pixels. Parameter: height The height of the plugin in pixels. Parameter: showHeader Show the 'Recent Activity' title bar. Parameter: colorScheme The color scheme of the plugin: 'light' or 'dark'. Parameter: font The font name for the plugin. Parameter: borderColor The border color of the plugin. Use common color names as Red, White, Black, and so on. Parameter: showRecommendations Whether to show recommendations on the activity feed. *@ @helper ActivityFeed( string site = "", int width = 300, int height = 300, bool showHeader = true, string colorScheme = "light", string font = "", string borderColor = "", bool showRecommendations = false) { if (site.IsEmpty()) { site = Request.Url.Host; } var src = new UrlBuilder("http://www.facebook.com/plugins/activity.php") .AddParam("site", site) .AddParam("width", width) .AddParam("height", height) .AddParam("header", showHeader) .AddParam("colorscheme", colorScheme) .AddParam("font", font) .AddParam("border_color", borderColor) .AddParam("recommendations", showRecommendations) .AddParam("locale", Language); } @* Summary: OpenGraph properties allows you to specify structured information about your web pages to show up your pages richly across Facebook and enable Facebook users to establish connections to your pages. Use this method to show OpenGraph page data, as the page title, URL, and so on. *NOTE*: It is important to notice that the OpenGraph properties are read directly from your site by Facebook, therefore your site needs to be published into a public address to see the OpenGraph properties working. Check this tutorial on publishing with WebMatrix: http://www.asp.net/webmatrix/tutorials/publish-a-website Parameter: siteName A human-readable name for your site, e.g., "Fourth Coffee". Parameter: title The title of your page as it should appear within Facebook, e.g., "Pecan Pie". Parameter: type The type of your page, e.g., "food". See the complete list of supported types here: http://developers.facebook.com/docs/opengraph#types Parameter: url The canonical URL of your object that will be used as its permanent ID in the graph, e.g., http://www.fourthcoffe.com/Product/1/. Parameter: imageUrl An image URL which should represent your page within the graph. The image must be at least 50px by 50px and have a maximum aspect ratio of 3:1. Parameter: description A one to two sentence description of your page. *@ @helper OpenGraphRequiredProperties( string siteName, string title, string type, string url, string imageUrl, string description = "") { if (!description.IsEmpty()) { } } @* Summary: OpenGraph properties allows you to specify structured information about your web pages to show up your pages richly across Facebook and enable Facebook users to establish connections to your pages. Use this method to show page location data. This is useful if your pages is a business profile or about anything else with a real-world location. You can specify location via latitude and longitude, a full address, or both. *NOTE*: It is important to notice that the OpenGraph properties are read directly from your site by Facebook, therefore your site needs to be published into a public address to see the OpenGraph properties working. Check this tutorial on publishing with WebMatrix: http://www.asp.net/webmatrix/tutorials/publish-a-website *@ @helper OpenGraphLocationProperties( string latitude = "", string longitude = "", string streetAddress = "", string locality = "", string region = "", string postalCode = "", string countryName = "") { if (!latitude.IsEmpty()) { } if (!longitude.IsEmpty()) { } if (!streetAddress.IsEmpty()) { } if (!locality.IsEmpty()) { } if (!region.IsEmpty()) { } if (!postalCode.IsEmpty()) { } if (!countryName.IsEmpty()) { } } @* Summary: OpenGraph properties allows you to specify structured information about your web pages to show up your pages richly across Facebook and enable Facebook users to establish connections to your pages. Use this method to show contact information about your page. Consider including contact information if your page is about an entity that can be contacted. *NOTE*: It is important to notice that the OpenGraph properties are read directly from your site by Facebook, therefore your site needs to be published into a public address to see the OpenGraph properties working. Check this tutorial on publishing with WebMatrix: http://www.asp.net/webmatrix/tutorials/publish-a-website *@ @helper OpenGraphContactProperties( string email = "", string phoneNumber = "", string faxNumber = "") { if (!email.IsEmpty()) { } if (!phoneNumber.IsEmpty()) { } if (!faxNumber.IsEmpty()) { } } @* Summary: Use this method inside your opening HTML tag for W3C compatibility. For example: *@ @helper FbmlNamespaces() {xmlns:fb="http://www.facebook.com/2008/fbml" xmlns:og="http://opengraphprotocol.org/schema/"} ================================================ FILE: src/Microsoft.Web.Helpers/FileUpload.cshtml ================================================ @* Generator: WebPagesHelper *@ @using System.Globalization @using System.Web @using Microsoft.Internal.Web.Utils @using Resources @functions { private class FileUploadTracker { private static readonly object _countKey = new object(); private static readonly object _scriptAlreadyRendered = new object(); private readonly HttpContextBase _httpContext; public FileUploadTracker(HttpContextBase httpContext) { _httpContext = httpContext; } public bool ScriptAlreadyRendered { get { bool? rendered = _httpContext.Items[_scriptAlreadyRendered] as bool?; return rendered.HasValue && rendered.Value; } set { _httpContext.Items[_scriptAlreadyRendered] = value; } } public int RenderCount { get { int? count = _httpContext.Items[_countKey] as int?; if (!count.HasValue) { count = 0; } return count.Value; } set { _httpContext.Items[_countKey] = value; } } } } @helper GetHtml(string name = null, int initialNumberOfFiles = 1, bool allowMoreFilesToBeAdded = true, bool includeFormTag = true, string addText = null, string uploadText = null) { @_GetHtml(new HttpContextWrapper(HttpContext.Current), name, initialNumberOfFiles, allowMoreFilesToBeAdded, includeFormTag, addText, uploadText) } @helper _GetHtml(HttpContextBase context, string name, int initialNumberOfFiles, bool allowMoreFilesToBeAdded, bool includeFormTag, string addText, string uploadText) { if (initialNumberOfFiles < 0) { throw new ArgumentOutOfRangeException( "initialNumberOfFiles", String.Format(CultureInfo.InvariantCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "0")); } var tracker = new FileUploadTracker(context); int count = tracker.RenderCount++; name = name ?? "fileUpload"; uploadText = uploadText ?? HelpersToolkitResources.FileUpload_Upload; addText = addText ?? HelpersToolkitResources.FileUpload_AddMore; if (allowMoreFilesToBeAdded && !tracker.ScriptAlreadyRendered) { tracker.ScriptAlreadyRendered = true; } if (includeFormTag) { @:
}
@for(int i = 0; i < initialNumberOfFiles; i++) {
}
if (allowMoreFilesToBeAdded || includeFormTag) {
@if (allowMoreFilesToBeAdded) { @addText } @if (includeFormTag) { }
} if (includeFormTag) { @:
} } ================================================ FILE: src/Microsoft.Web.Helpers/GamerCard.cshtml ================================================ @* Generator: WebPagesHelper *@ @using Microsoft.Internal.Web.Utils @helper GetHtml(string gamerTag) { if (gamerTag.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "gamerTag"); } } ================================================ FILE: src/Microsoft.Web.Helpers/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#GetInitializationScripts()", Justification = "It is analogous to the get pattern users are familiar with")] [assembly: SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#GetFacebookUserProfile()", Justification = "It is analogous to the get pattern users are familiar with")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.Analytics", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.Bing", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.Facebook", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.FileUpload", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.GamerCard", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.LinkShare", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.Maps", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors", Scope = "type", Target = "Microsoft.Web.Helpers.ReCaptcha", Justification = "This is the default format in which helpers are generated.")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButton(System.String,System.String,System.String,System.String,System.Boolean,System.String,System.String,System.Boolean,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "4#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#OpenGraphRequiredProperties(System.String,System.String,System.String,System.String,System.String,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButton(System.String,System.String,System.String,System.String,System.Boolean,System.String,System.String,System.Boolean,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButton(System.String,System.String,System.String,System.String,System.Boolean,System.String,System.String,System.Boolean,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Scope = "member", Target = "Microsoft.Web.Helpers.Bing.#SearchBox(System.String,System.String,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#OpenGraphRequiredProperties(System.String,System.String,System.String,System.String,System.String,System.String)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LiveStream(System.Int32,System.Int32,System.String,System.String,System.Boolean)", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Scope = "member", Target = "Microsoft.Web.Helpers.Bing.#SiteUrl", Justification = "We prefer strings to URIs for helpers")] [assembly: SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.Web.Helpers.Facebook+UserProfile", Justification = "The type is consumed but never instantiated by a user")] [assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Scope = "member", Target = "Microsoft.Web.Helpers.Maps.#GetBingHtml(System.String,System.String,System.String,System.String,System.String,System.String,System.Int32,System.String,System.Boolean,System.String,System.Collections.Generic.IEnumerable`1)", Justification = "We're printing a JSON value that needs to be lower case")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Facepile", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#Facepile(System.Int32,System.Int32)", Justification = "Facebook related term")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Fbml", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#FbmlNamespaces()", Justification = "Facebook related term")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "num", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#Comments(System.String,System.Int32,System.Int32,System.Boolean,System.Boolean)", Justification = "num is not Hungarian notation")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xid", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#Comments(System.String,System.Int32,System.Int32,System.Boolean,System.Boolean)", Justification = "Facebook related term")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xid", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LiveStream(System.Int32,System.Int32,System.String,System.String,System.Boolean)", Justification = "Facebook related term")] [assembly: SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Re", Scope = "type", Target = "Microsoft.Web.Helpers.ReCaptcha")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButton(System.String,System.String,System.String,System.String,System.Boolean,System.String,System.String,System.Boolean,System.String)", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButtonTagOnly(System.String,System.Boolean,System.String,System.String,System.String,System.Boolean,System.String)", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButtonTagOnly(System.String,System.Boolean,System.String,System.String,System.String,System.Boolean,System.String)", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#LoginButton(System.String,System.String,System.String,System.String,System.Boolean,System.String,System.String,System.Boolean,System.String)", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook.#MembershipLogin()", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Scope = "member", Target = "Microsoft.Web.Helpers.LinkShare.#BitlyLogin", Justification = "We use login and logout in WebMatrix.Security")] [assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Web.Helpers.Bing.#SiteUrl", Justification = "Property name is used instead of the generic term value to make it simpler to debug.")] [assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Web.Helpers.GamerCard.#GetHtml(System.String)")] [assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Microsoft.Web.Helpers.Bing.#SiteTitle", Justification = "Property name is used instead of the generic term value to make it simpler to debug.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook+UserProfile.#Updated_Time", Justification = "This is serailzed from a JSON schema, so member names have to be exactly this way.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook+UserProfile.#Last_Name", Justification = "This is serailzed from a JSON schema, so member names have to be exactly this way.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook+UserProfile.#First_Name", Justification = "This is serailzed from a JSON schema, so member names have to be exactly this way.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Timezone", Scope = "member", Target = "Microsoft.Web.Helpers.Facebook+UserProfile.#Timezone", Justification = "This is serailzed from a JSON schema, so member names have to be exactly this way.")] ================================================ FILE: src/Microsoft.Web.Helpers/Gravatar.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Text; using System.Web; using System.Web.Helpers; using Microsoft.Internal.Web.Utils; using Resources; namespace Microsoft.Web.Helpers { public static class Gravatar { private const string GravatarUrl = "http://www.gravatar.com/avatar/"; // review - extract conversion of anonymous object to html attributes string into separate helper public static HtmlString GetHtml(string email, int imageSize = 80, string defaultImage = null, GravatarRating rating = GravatarRating.Default, string imageExtension = null, object attributes = null) { bool altSpecified = false; string url = GetUrl(email, imageSize, defaultImage, rating, imageExtension); StringBuilder html = new StringBuilder(String.Format(CultureInfo.InvariantCulture, " p.Name)) { if (!p.Name.Equals("src", StringComparison.OrdinalIgnoreCase)) { object value = p.GetValue(attributes, null); if (value != null) { string encodedValue = HttpUtility.HtmlAttributeEncode(value.ToString()); html.Append(String.Format(CultureInfo.InvariantCulture, "{0}=\"{1}\" ", p.Name, encodedValue)); } if (p.Name.Equals("alt", StringComparison.OrdinalIgnoreCase)) { altSpecified = true; } } } } if (!altSpecified) { html.Append("alt=\"gravatar\" "); } html.Append("/>"); return new HtmlString(html.ToString()); } // See: http://en.gravatar.com/site/implement/url [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Strings are easier to work with for Plan9 scenario")] [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Gravatar.com requires lowercase")] public static string GetUrl(string email, int imageSize = 80, string defaultImage = null, GravatarRating rating = GravatarRating.Default, string imageExtension = null) { if (String.IsNullOrEmpty(email)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "email"); } if ((imageSize <= 0) || (imageSize > 512)) { throw new ArgumentException(HelpersToolkitResources.Gravatar_InvalidImageSize, "imageSize"); } StringBuilder url = new StringBuilder(GravatarUrl); email = email.Trim().ToLowerInvariant(); url.Append(Crypto.Hash(email, algorithm: "md5").ToLowerInvariant()); if (!String.IsNullOrEmpty(imageExtension)) { if (!imageExtension.StartsWith(".", StringComparison.Ordinal)) { url.Append('.'); } url.Append(imageExtension); } url.Append("?s="); url.Append(imageSize); if (rating != GravatarRating.Default) { url.Append("&r="); url.Append(rating.ToString().ToLowerInvariant()); } if (!String.IsNullOrEmpty(defaultImage)) { url.Append("&d="); url.Append(HttpUtility.UrlEncode(defaultImage)); } return HttpUtility.HtmlAttributeEncode(url.ToString()); } } } ================================================ FILE: src/Microsoft.Web.Helpers/GravatarRating.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace Microsoft.Web.Helpers { public enum GravatarRating { Default, [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "G", Justification = "Matches the gravatar.com rating. Suppressed in source because this is a one-time occurrence")] G, PG, [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "R", Justification = "Matches the gravatar.com rating. Suppressed in source because this is a one-time occurrence")] R, [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X", Justification = "Matches the gravatar.com rating. Suppressed in source because this is a one-time occurrence")] X } } ================================================ FILE: src/Microsoft.Web.Helpers/LinkShare.cshtml ================================================ @* Generator: WebPagesHelper *@ @using System.Globalization @using System.Text @using System.Web.WebPages.Scope @using Microsoft.Internal.Web.Utils @using Resources; @functions { internal static readonly object _bitlyApiKey = new object(); internal static readonly object _bitlyLogin = new object(); private static readonly Lazy> _allSites = new Lazy>(() => from site in (LinkShareSite[])Enum.GetValues(typeof(LinkShareSite)) #pragma warning disable 0618 where site != LinkShareSite.All #pragma warning restore 0618 select site ); public static string BitlyApiKey { get { return ScopeStorage.CurrentScope[_bitlyApiKey] as string; } set { if (value == null) { throw new ArgumentNullException("value"); } ScopeStorage.CurrentScope[_bitlyApiKey] = value; } } public static string BitlyLogin { get { return ScopeStorage.CurrentScope[_bitlyLogin] as string; } set { if (value == null) { throw new ArgumentNullException("value"); } ScopeStorage.CurrentScope[_bitlyLogin] = value; } } private static string GetShortenedUrl(string pageLinkBack) { if (BitlyLogin.IsEmpty() || BitlyApiKey.IsEmpty()) { return pageLinkBack; } string encodedPageLinkBack = HttpUtility.UrlEncode(pageLinkBack); string key = "Bitly_pageLinkBack_" + BitlyApiKey + "_" + encodedPageLinkBack; string shortUrl = WebCache.Get(key) as string; if (shortUrl != null) { return shortUrl; } string bitlyReq = "http://api.bit.ly/v3/shorten?format=txt&longUrl=" + encodedPageLinkBack + "&login=" + BitlyLogin + "&apiKey=" + BitlyApiKey; try { shortUrl = GetWebResponse(bitlyReq); } catch (WebException) { return pageLinkBack; } if (shortUrl != null) { WebCache.Set(key, shortUrl); return shortUrl; } return pageLinkBack; } private static string GetWebResponse(string address) { WebRequest request = WebRequest.Create(address); request.Method = "GET"; request.Timeout = 5 * 1000; //5 seconds using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode != HttpStatusCode.OK) { return null; } using (Stream stream = response.GetResponseStream()) { using (MemoryStream memStream = new MemoryStream()) { stream.CopyTo(memStream); // Review: Should we use the ContentEncoding from response? return Encoding.UTF8.GetString(memStream.ToArray()); } } } } /// /// Returns an ordered list of LinkShareSite based on position of "All" parameter occurs in the list. /// /// /// The LinkShareSite is accepted as a params array. /// In the event that no value is provided or the LinkShareSite.All is the first param, we display all the sites in the order they appear in the enum. /// If not, the items we look for the first occurence of LinkShareSite.All in the array. /// The items that appear before this appear in the order they are specified. The All is replaced by all items in the enum that were not already specified by the user /// in the order they appear in the enum. /// e.g. sites = [] { Twitter, Facebook, Digg, All } /// Would result in returning {Twitter, Facebook, Digg, Delicious, Reddit, StumbleUpon} /// internal static IEnumerable GetSitesInOrder(LinkShareSite[] linkSites) { var allSites = _allSites.Value; if (linkSites == null || !linkSites.Any() || linkSites.First() == LinkShareSite.All) { // Show all sites return allSites; } var result = linkSites.TakeWhile(c => c != LinkShareSite.All).ToList(); if (result.Count != linkSites.Length) { return Enumerable.Concat(result, allSites.Except(result)); } else { return result; } } private static void ConstructPageLinkBack(ref string pageLinkBack, out string shortenedUrl) { HttpContext context = HttpContext.Current; if ((pageLinkBack == null) && (context != null)) { pageLinkBack = context.Request.Url.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped); } shortenedUrl = GetShortenedUrl(pageLinkBack); } } @helper GetHtml(string pageTitle, string pageLinkBack = null, string twitterUserName = null, string additionalTweetText = null, params LinkShareSite[] linkSites) { if (pageTitle.IsEmpty()) { throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "pageTitle"), "pageTitle"); } string shortenedUrl; ConstructPageLinkBack(ref pageLinkBack, out shortenedUrl); pageLinkBack = HttpUtility.UrlEncode(pageLinkBack); shortenedUrl = HttpUtility.UrlEncode(shortenedUrl); pageTitle = HttpUtility.UrlEncode(pageTitle); foreach (var site in GetSitesInOrder(linkSites)) { switch (site) { case LinkShareSite.Delicious: Add to del.icio.us break; case LinkShareSite.Digg: Digg! break; case LinkShareSite.Facebook: Share on Facebook break; case LinkShareSite.Reddit: Reddit! break; case LinkShareSite.StumbleUpon: Stumble it! break; case LinkShareSite.Twitter: string status = String.Empty; if (!twitterUserName.IsEmpty()) { status += ", (via @@" + twitterUserName + ")"; } if (!additionalTweetText.IsEmpty()) { status += ' ' + additionalTweetText; } status = HttpUtility.UrlEncode(status); Share on Twitter break; default: throw new NotSupportedException(String.Format(CultureInfo.CurrentUICulture, HelpersToolkitResources.LinkShareValue_NotSupported, site)); } } } ================================================ FILE: src/Microsoft.Web.Helpers/LinkShareSite.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Helpers { public enum LinkShareSite { Delicious = 0, Digg = 1, // GoogleBuzz = 2, has been deprecated Facebook = 3, Reddit = 4, StumbleUpon = 5, Twitter = 6, All = 7, } } ================================================ FILE: src/Microsoft.Web.Helpers/Maps.cshtml ================================================ @* Generator : WebPagesHelper *@ @using System.Diagnostics @using System.Web.WebPages.Scope @using System.Web.UI.WebControls @using System.Globalization @using Microsoft.Internal.Web.Utils @functions { private const string DefaultWidth = "300px"; private const string DefaultHeight = "300px"; private static readonly object _mapIdKey = new object(); private static readonly object _mapQuestApiKey = new object(); private static readonly object _bingApiKey = new object(); private static readonly object _yahooApiKey = new object(); public static string MapQuestApiKey { get { return (string)ScopeStorage.CurrentScope[_mapQuestApiKey]; } set { ScopeStorage.CurrentScope[_mapQuestApiKey] = value; } } public static string YahooApiKey { get { return (string)ScopeStorage.CurrentScope[_yahooApiKey]; } set { ScopeStorage.CurrentScope[_yahooApiKey] = value; } } public static string BingApiKey { get { return (string)ScopeStorage.CurrentScope[_bingApiKey]; } set { ScopeStorage.CurrentScope[_bingApiKey] = value; } } // allow for stubbing this static resource in tests private static Func _getCurrentHttpContext = (Func)(() => new HttpContextWrapper(HttpContext.Current)); internal static Func GetCurrentHttpContext { private get { return _getCurrentHttpContext; } set { _getCurrentHttpContext = value; } } private static int MapId { get { var value = (int?)(GetCurrentHttpContext().Items[_mapIdKey]); return value.GetValueOrDefault(); } set { GetCurrentHttpContext().Items[_mapIdKey] = value; } } private static string GetMapElementId() { return "map_" + MapId; } private static string TryParseUnit(string value, string defaultValue) { if (String.IsNullOrEmpty(value)) { return defaultValue; } try { return Unit.Parse(value, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); } catch (ArgumentException) { return defaultValue; } } private static IHtmlString RawJS(string text) { return Raw(HttpUtility.JavaScriptStringEncode(text)); } private static IHtmlString Raw(string text) { return new HtmlString(text); } private static string GetApiKey(string apiKey, object scopeStorageKey) { if (apiKey.IsEmpty()) { return (string)ScopeStorage.CurrentScope[scopeStorageKey]; } return apiKey; } public class MapLocation { private readonly string _latitude; private readonly string _longitude; public MapLocation(string latitude, string longitude) { _latitude = latitude; _longitude = longitude; } public string Latitude { get { return _latitude; } } public string Longitude { get { return _longitude; } } } internal static string GetDirectionsQuery(string location, string latitude, string longitude, Func encoder = null) { encoder = encoder ?? HttpUtility.UrlEncode; Debug.Assert(!(location.IsEmpty() && latitude.IsEmpty() && longitude.IsEmpty())); if (location.IsEmpty()) { return encoder(latitude + "," + longitude); } return encoder(location); } } @** Summary: Generates Html to display a Map Quest map. Parameter: key: Map Quest API key Parameter location: Address of the location to center the map at Parameter latitude: Latitude to center on. If both latitude and longitude are specified, location is ignored. Parameter longitude: Longitude to center on. If both latitude and longitude are specified, location is ignored. Parameter width: Width of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter height: Height of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter zoom: Initial zoom level of the map. Defaults to 7. Parameter type: Map type to display. Valid values are "map", "sat" or "hyb". Parameter showDirectionsLink: Determines if a link to get directions should be displayed when a location is specified. Defaults to true. Parameter directionsLinkText The text for the get directions link. Defaults to "Get Directions". **@ @helper GetMapQuestHtml(string key = null, string location = null, string latitude = null, string longitude = null, string width = "300px", string height = "300px", int zoom = 7, string type = "map", bool showDirectionsLink = true, string directionsLinkText = "Get Directions", bool showZoomControl = true, IEnumerable pushpins = null) { key = GetApiKey(key, _mapQuestApiKey); if (key.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "key"); } string mapElement = GetMapElementId(); string loc = "null"; // We want to print the value 'null' in the client if (latitude != null && longitude != null) { loc = String.Format(CultureInfo.InvariantCulture, "{{lat: {0}, lng: {1}}}", HttpUtility.JavaScriptStringEncode(latitude, addDoubleQuotes: false), HttpUtility.JavaScriptStringEncode(longitude, addDoubleQuotes: false)); } // The MapQuest key listed on their website is Url encoded to begin with.
if (showDirectionsLink) { @directionsLinkText } MapId++; } @** Summary: Generates Html to display Bing map. Parameter: key: Bing Maps application key Parameter location: Address of the location to center the map at Parameter latitude: Latitude to center on. If both latitude and longitude are specified, location is ignored. Parameter longitude: Longitude to center on. If both latitude and longitude are specified, location is ignored. Parameter width: Width of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter height: Height of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter zoom: Initial zoom level of the map. Defaults to 14. Parameter type: Map type to display. Valid values are "auto", "aerial", "birdeye", "road" and "mercator". Parameter useAdaptiveOverlay: Determines if the map overlay that adapts to the colors of the current map view is used. Defaults to true. Parameter showDirectionsLink: Determines if a link to get directions should be displayed when a location is specified. Defaults to true. Parameter directionsLinkText The text for the get directions link. Defaults to "Get Directions". **@ @helper GetBingHtml(string key = null, string location = null, string latitude = null, string longitude = null, string width = null, string height = null, int zoom = 14, string type = "auto", bool useAdaptiveOverlay = true, bool showDirectionsLink = true, string directionsLinkText = "Get Directions", IEnumerable pushpins = null) { key = GetApiKey(key, _bingApiKey); if (key.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "key"); } string mapElement = GetMapElementId(); type = (type ?? "auto").ToLowerInvariant();
if (showDirectionsLink) { // Review: Need to figure out if the link needs to be localized. @directionsLinkText } MapId++; } @** Summary: Generates Html to display a Google map. Parameter location: Address of the location to center the map at Parameter latitude: Latitude to center on. If both latitude and longitude are specified, location is ignored. Parameter longitude: Longitude to center on. If both latitude and longitude are specified, location is ignored. Parameter width: Width of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter height: Height of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter zoom: Initial zoom level of the map. Defaults to 14. Parameter type: Map type to display. Valid values are "ROADMAP", "HYBRID", "SATELLITE" and "TERRAIN". Parameter showDirectionsLink: Determines if a link to get directions should be displayed when a location is specified. Defaults to true. Parameter directionsLinkText The text for the get directions link. Defaults to "Get Directions". **@ @helper GetGoogleHtml(string location = null, string latitude = null, string longitude = null, string width = null, string height = null, int zoom = 14, string type = "ROADMAP", bool showDirectionsLink = true, string directionsLinkText = "Get Directions", IEnumerable pushpins = null) { string mapElement = GetMapElementId(); type = (type ?? "ROADMAP").ToUpperInvariant(); // Map types are in upper case // Google maps does not support null centers. We'll set it to arbitrary values if they are null and only the location is provided. // These locations are somewhere around Microsoft's Redmond Campus. latitude = latitude ?? "47.652437"; longitude = longitude ?? "-122.132424";
if (showDirectionsLink) { @directionsLinkText } MapId++; } @** Summary: Generates Html to display a Yahoo map. Parameter: key: Yahoo application ID Parameter location: Address of the location to center the map at Parameter latitude: Latitude to center on. If both latitude and longitude are specified, location is ignored. Parameter longitude: Longitude to center on. If both latitude and longitude are specified, location is ignored. Parameter width: Width of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter height: Height of the map with units such as 480px, 100% etc. Defaults to 300px. Parameter zoom: Initial zoom level of the map. Defaults to 4. Parameter type: Map type to display. Valid values are "YAHOO_MAP_SAT", "YAHOO_MAP_HYB" and "YAHOO_MAP_REG". Parameter showDirectionsLink: Determines if a link to get directions should be displayed when a location is specified. Defaults to true. Parameter directionsLinkText The text for the get directions link. Defaults to "Get Directions". **@ @helper GetYahooHtml(string key = null, string location = null, string latitude = null, string longitude = null, string width = null, string height = null, int zoom = 4, string type = "YAHOO_MAP_REG", bool showDirectionsLink = true, string directionsLinkText = "Get Directions", IEnumerable pushpins = null) { key = GetApiKey(key, _yahooApiKey); if (key.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "key"); } string mapElement = GetMapElementId();
if (showDirectionsLink) { @directionsLinkText } MapId++; } ================================================ FILE: src/Microsoft.Web.Helpers/Microsoft.Web.Helpers.csproj ================================================  {0C7CE809-0F72-4C19-8C64-D6573E4D9521} Library Properties Microsoft.Web.Helpers Microsoft.Web.Helpers $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\WebHelpers.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True HelpersToolkitResources.resx Code Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator HelpersToolkitResources.Designer.cs Resources {9B7E3740-6161-4548-833C-4BBCA43B970E} System.Web.Helpers {8F18041B-9410-4C36-A9C5-067813DF5F31} System.Web.Razor {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} System.Web.WebPages.Razor {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {4D39BAAF-8A96-473E-AB79-C8A341885137} WebMatrix.Data {55A15F40-1435-4248-A7F2-2A146BB83586} WebMatrix.WebData ================================================ FILE: src/Microsoft.Web.Helpers/PreApplicationStartCode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.WebPages.Razor; namespace Microsoft.Web.Helpers { [EditorBrowsable(EditorBrowsableState.Never)] public static class PreApplicationStartCode { private static bool _startWasCalled; public static void Start() { // Even though ASP.NET will only call each PreAppStart once, we sometimes internally call one PreAppStart from // another PreAppStart to ensure that things get initialized in the right order. ASP.NET does not guarantee the // order so we have to guard against multiple calls. // All Start calls are made on same thread, so no lock needed here. if (_startWasCalled) { return; } _startWasCalled = true; // Auto import the Microsoft.Web.Helpers namespace to all apps that are executing. WebPageRazorHost.AddGlobalImport(typeof(PreApplicationStartCode).Namespace); } } } ================================================ FILE: src/Microsoft.Web.Helpers/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using Microsoft.Web.Helpers; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Microsoft.Web.Helpers")] [assembly: AssemblyDescription("")] [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] [assembly: InternalsVisibleTo("Microsoft.Web.Helpers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] ================================================ FILE: src/Microsoft.Web.Helpers/ReCaptcha.cshtml ================================================ @* Generator: WebPagesHelper *@ @using System @using System.Globalization @using System.IO @using System.Text @using System.Web.WebPages.Scope @using Microsoft.Internal.Web.Utils @using Resources @functions{ private const string _reCaptchaUrl = "http://www.google.com/recaptcha/api"; private const string _reCaptchaSecureUrl = "https://www.google.com/recaptcha/api"; private static readonly object _errorCodeCacheKey = new object(); internal static readonly object _privateKey = new object(); internal static readonly object _publicKey = new object(); public static string PrivateKey { get { return ScopeStorage.CurrentScope[_privateKey] as string; } set { if (value == null) { throw new ArgumentNullException("value"); } ScopeStorage.CurrentScope[_privateKey] = value; } } public static string PublicKey { get { return ScopeStorage.CurrentScope[_publicKey] as string; } set { if (value == null) { throw new ArgumentNullException("value"); } ScopeStorage.CurrentScope[_publicKey] = value; } } public static bool Validate(string privateKey = null) { return Validate(HttpContext.Current == null ? null : new HttpContextWrapper(HttpContext.Current), privateKey, UrlBuilder.DefaultVirtualPathUtility); } internal static string GetLastError(HttpContextBase context) { if (context.Items.Contains(_errorCodeCacheKey)) { return context.Items[_errorCodeCacheKey] as string; } return String.Empty; } internal static bool Validate(HttpContextBase context, string privateKey, VirtualPathUtilityBase virtualPathUtility) { privateKey = privateKey ?? PrivateKey; if (String.IsNullOrEmpty(privateKey)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "privateKey"); } SetLastError(context, String.Empty); string postedBody = GetValidatePostData(context, privateKey, virtualPathUtility); if (String.IsNullOrEmpty(postedBody)) { return false; } string result = ExecuteValidateRequest(postedBody); return HandleValidateResponse(context, result); } internal static string GetValidatePostData(HttpContextBase context, string privateKey, VirtualPathUtilityBase virtualPathUtility) { string remoteIP = context.Request.ServerVariables["REMOTE_ADDR"]; if (String.IsNullOrEmpty(remoteIP)) { throw new InvalidOperationException(HelpersToolkitResources.ReCaptcha_RemoteIPNotFound); } // Noscript rendering requires the user to copy and paste the challenge string to a textarea. // When the challenge is invalid the recaptcha service doesn't return an error that affects // UI rendering, so Validate should just return false without issuing the web request. string challenge = context.Request.Form["recaptcha_challenge_field"]; if (String.IsNullOrEmpty(challenge)) { return String.Empty; } string response = (context.Request.Form["recaptcha_response_field"] ?? String.Empty).Trim(); var builder = new UrlBuilder(context, virtualPathUtility, path: null, parameters: null) .AddParam("privatekey", privateKey) .AddParam("remoteip", context.Request.ServerVariables["REMOTE_ADDR"]) .AddParam("challenge", challenge) .AddParam("response", response); // Trim the leading ? and return the QueryString return builder.QueryString.Substring(1); } internal static bool HandleValidateResponse(HttpContextBase context, string response) { if (!String.IsNullOrEmpty(response)) { string[] results = response.Split('\n'); if (results.Length > 0) { bool rval = Convert.ToBoolean(results[0], CultureInfo.InvariantCulture); if (!rval && (results.Length > 1)) { SetLastError(context, results[1]); } return rval; } } return false; } internal static string GetChallengeUrl(HttpContextBase httpContext, string publicKey = null, string errorCode = null) { return GetUrlHelper(httpContext, "challenge", publicKey, errorCode: errorCode); } private static string ExecuteValidateRequest(string formData) { WebRequest request = WebRequest.Create(_reCaptchaUrl + "/verify"); request.Method = "POST"; request.Timeout = 5000; //milliseconds request.ContentType = "application/x-www-form-urlencoded"; byte[] content = Encoding.UTF8.GetBytes(formData); using (Stream stream = request.GetRequestStream()) { stream.Write(content, 0, content.Length); } using (WebResponse response = request.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream())) { return reader.ReadToEnd(); } } } private static string GetUrlHelper(HttpContextBase context, string path, string publicKey, string errorCode) { publicKey = publicKey ?? PublicKey; if (String.IsNullOrEmpty(publicKey)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "publicKey"); } var builder = new UrlBuilder(context.Request.IsSecureConnection ? _reCaptchaSecureUrl : _reCaptchaUrl); builder.AddPath(path); builder.AddParam("k", publicKey); if (!String.IsNullOrEmpty(errorCode)) { builder.AddParam("error", errorCode); } return builder; } private static void SetLastError(HttpContextBase context, string value) { context.Items[_errorCodeCacheKey] = value; } } @helper GetHtml(string publicKey = null, string theme = "red", string language = "en", int tabIndex = 0) { @GetHtmlWithOptions(publicKey, options: new Dictionary() { { "theme", theme }, { "lang", language }, { "tabindex", tabIndex } }) } @helper GetHtmlWithOptions(string publicKey = null, object options = null) { @GetHtml(HttpContext.Current == null ? null : new HttpContextWrapper(HttpContext.Current), publicKey, options) } @helper GetHtml(HttpContextBase httpContext, string publicKey = null, object options = null) { if (options != null) { var optionJson = new HtmlString(Json.Encode(options)); } } ================================================ FILE: src/Microsoft.Web.Helpers/Resources/HelpersToolkitResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.18051 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class HelpersToolkitResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal HelpersToolkitResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Web.Helpers.Resources.HelpersToolkitResources", typeof(HelpersToolkitResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Add more files. /// internal static string FileUpload_AddMore { get { return ResourceManager.GetString("FileUpload_AddMore", resourceCulture); } } /// /// Looks up a localized string similar to Upload. /// internal static string FileUpload_Upload { get { return ResourceManager.GetString("FileUpload_Upload", resourceCulture); } } /// /// Looks up a localized string similar to The Gravatar image size must be between 1 and 512 pixels.. /// internal static string Gravatar_InvalidImageSize { get { return ResourceManager.GetString("Gravatar_InvalidImageSize", resourceCulture); } } /// /// Looks up a localized string similar to LinkShare value {0} is not supported by the Link Share helper. . /// internal static string LinkShareValue_NotSupported { get { return ResourceManager.GetString("LinkShareValue_NotSupported", resourceCulture); } } /// /// Looks up a localized string similar to The captcha cannot be validated because the remote address was not found in the request.. /// internal static string ReCaptcha_RemoteIPNotFound { get { return ResourceManager.GetString("ReCaptcha_RemoteIPNotFound", resourceCulture); } } /// /// Looks up a localized string similar to You cannot have a null folder. Try using Themes.GetResourcePath(string fileName) instead if you do not want to specify a folder.. /// internal static string Themes_FolderCannotBeNull { get { return ResourceManager.GetString("Themes_FolderCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to Unknown theme '{0}'. Ensure that a directory labeled '{0}' exists under the theme directory.. /// internal static string Themes_InvalidTheme { get { return ResourceManager.GetString("Themes_InvalidTheme", resourceCulture); } } /// /// Looks up a localized string similar to You must call the "Themes.Initialize" method before you call any other method of the "Themes" class.. /// internal static string Themes_NotInitialized { get { return ResourceManager.GetString("Themes_NotInitialized", resourceCulture); } } /// /// Looks up a localized string similar to The media file "{0}" does not exist.. /// internal static string Video_FileDoesNotExist { get { return ResourceManager.GetString("Video_FileDoesNotExist", resourceCulture); } } /// /// Looks up a localized string similar to Property "{0}" cannot be set through this argument.. /// internal static string Video_PropertyCannotBeSet { get { return ResourceManager.GetString("Video_PropertyCannotBeSet", resourceCulture); } } } } ================================================ FILE: src/Microsoft.Web.Helpers/Resources/HelpersToolkitResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Add more files Upload The Gravatar image size must be between 1 and 512 pixels. LinkShare value {0} is not supported by the Link Share helper. The captcha cannot be validated because the remote address was not found in the request. You cannot have a null folder. Try using Themes.GetResourcePath(string fileName) instead if you do not want to specify a folder. Unknown theme '{0}'. Ensure that a directory labeled '{0}' exists under the theme directory. You must call the "Themes.Initialize" method before you call any other method of the "Themes" class. The media file "{0}" does not exist. Property "{0}" cannot be set through this argument. ================================================ FILE: src/Microsoft.Web.Helpers/Themes.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Web.Hosting; using System.Web.WebPages.Scope; namespace Microsoft.Web.Helpers { public static class Themes { public static string ThemeDirectory { get { return Implementation.ThemeDirectory; } } public static string CurrentTheme { get { return Implementation.CurrentTheme; } set { Implementation.CurrentTheme = value; } } public static string DefaultTheme { get { return Implementation.DefaultTheme; } } public static ReadOnlyCollection AvailableThemes { get { return Implementation.AvailableThemes; } } private static ThemesImplementation Implementation { get { return new ThemesImplementation(HostingEnvironment.VirtualPathProvider, ScopeStorage.CurrentScope); } } public static void Initialize(string themeDirectory, string defaultTheme) { Implementation.Initialize(themeDirectory, defaultTheme); } /// /// Get a file that lives directly inside the theme directory /// /// The filename to look for /// The full path to the file that matches the requested file public static string GetResourcePath(string fileName) { return Implementation.GetResourcePath(fileName); } public static string GetResourcePath(string folder, string fileName) { return Implementation.GetResourcePath(folder, fileName); } } } ================================================ FILE: src/Microsoft.Web.Helpers/ThemesImplementation.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Web.Hosting; using Microsoft.Internal.Web.Utils; using Resources; namespace Microsoft.Web.Helpers { internal class ThemesImplementation { internal static readonly object CurrentThemeKey = new object(); internal static readonly object ThemeDirectoryKey = new object(); internal static readonly object DefaultThemeKey = new object(); internal static readonly object ThemesInitializedKey = new object(); private readonly VirtualPathProvider _vpp; private readonly IDictionary _currentScope; public ThemesImplementation(VirtualPathProvider vpp, IDictionary scopeStorage) { _vpp = vpp; _currentScope = scopeStorage; } public string ThemeDirectory { get { EnsureInitialized(); return (string)_currentScope[ThemeDirectoryKey]; } private set { Debug.Assert(value != null); _currentScope[ThemeDirectoryKey] = value; } } /// /// This should live throughout the application life cycle /// and be set in _appstart.cshtml /// public string DefaultTheme { get { EnsureInitialized(); return (string)_currentScope[DefaultThemeKey]; } private set { Debug.Assert(value != null); _currentScope[DefaultThemeKey] = value; } } /// /// The current theme to use. When this is set, /// all GetResource checks will check if the CurrentTheme /// contains the file, and if it doesn't it will fall back to /// the DefaultTheme /// public string CurrentTheme { get { EnsureInitialized(); return (string)_currentScope[CurrentThemeKey] ?? DefaultTheme; } set { if (String.IsNullOrEmpty(value)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "value"); } // EnsureValidTheme would verify if themes have been correctly initialized and that the value specified is a valid theme. if (!IsValidTheme(AvailableThemes, value)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersToolkitResources.Themes_InvalidTheme, value), "value"); } _currentScope[CurrentThemeKey] = value; } } public ReadOnlyCollection AvailableThemes { get { EnsureInitialized(); return GetAvailableThemes(ThemeDirectory); } } private string CurrentThemePath { get { return Path.Combine(ThemeDirectory, CurrentTheme); } } private string DefaultThemePath { get { return Path.Combine(ThemeDirectory, DefaultTheme); } } private bool ThemesInitialized { get { bool? value = (bool?)_currentScope[ThemesInitializedKey]; return value != null && value.Value; } set { _currentScope[ThemesInitializedKey] = value; } } public void Initialize(string themeDirectory, string defaultTheme) { if (String.IsNullOrEmpty(themeDirectory)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "themeDirectory"); } if (String.IsNullOrEmpty(defaultTheme)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "defaultTheme"); } var availableThemes = GetAvailableThemes(themeDirectory); if (!IsValidTheme(availableThemes, defaultTheme)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersToolkitResources.Themes_InvalidTheme, defaultTheme), "defaultTheme"); } ThemeDirectory = themeDirectory; DefaultTheme = defaultTheme; ThemesInitialized = true; } /// /// Get a file that lives directly inside the theme directory /// /// The filename to look for /// The full path to the file that matches the requested file public string GetResourcePath(string fileName) { return GetResourcePath(String.Empty, fileName); } public string GetResourcePath(string folder, string fileName) { EnsureInitialized(); if (folder == null) { throw new ArgumentNullException("folder", HelpersToolkitResources.Themes_FolderCannotBeNull); } if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "fileName"); } return FindMatchingFile(Path.Combine(CurrentThemePath, folder), fileName) ?? FindMatchingFile(Path.Combine(DefaultThemePath, folder), fileName); } /// /// Try and find a file in the specified folder that matches name. /// /// The full path to the file that matches the requested file /// or null if no matching file is found internal string FindMatchingFile(string folder, string name) { Debug.Assert(!String.IsNullOrEmpty(folder)); Debug.Assert(!String.IsNullOrEmpty(name)); // Get the virtual path information VirtualDirectory directory = _vpp.GetDirectory(folder); // If the folder specified doesn't exist // or it doesn't contain any files if (directory == null || directory.Files == null) { return null; } // Go through every file in the directory foreach (VirtualFile file in directory.Files) { string path = file.VirtualPath; // Compare the filename to the filename that we passed if (Path.GetFileName(path).Equals(name, StringComparison.OrdinalIgnoreCase)) { return path; } } // If no matching files, return null return null; } private ReadOnlyCollection GetAvailableThemes(string themesRoot) { VirtualDirectory directory = _vpp.GetDirectory(themesRoot); var themes = new List(); // Go through every file in the directory foreach (VirtualDirectory dir in directory.Directories) { themes.Add(dir.Name); } return themes.AsReadOnly(); } private void EnsureInitialized() { if (!ThemesInitialized) { throw new InvalidOperationException(HelpersToolkitResources.Themes_NotInitialized); } } private static bool IsValidTheme(IEnumerable availableThemes, string theme) { return availableThemes.Contains(theme, StringComparer.OrdinalIgnoreCase); } } } ================================================ FILE: src/Microsoft.Web.Helpers/UrlBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Text; using System.Web; using System.Web.Routing; using System.Web.WebPages; namespace Microsoft.Web.Helpers { public class UrlBuilder { private static readonly VirtualPathUtilityWrapper _defaultVirtualPathUtility = new VirtualPathUtilityWrapper(); private readonly VirtualPathUtilityBase _virtualPathUtility; private readonly StringBuilder _params = new StringBuilder(); private string _path; /// /// Constructs an Url with the current page's virtual path and no query string parameters /// public UrlBuilder() : this(null, null) { } /// /// Constructs an Url with the specified path and no query string parameters. /// public UrlBuilder(string path) : this(path, null) { } /// /// Constructs an Url with the current page's virtual path and the parameters /// /// public UrlBuilder(object parameters) : this(null, parameters) { } public UrlBuilder(string path, object parameters) : this(GetHttpContext(), null, path, parameters) { } internal UrlBuilder(HttpContextBase httpContext, VirtualPathUtilityBase virtualPathUtility, string path, object parameters) { _virtualPathUtility = virtualPathUtility; Uri uri; if (Uri.TryCreate(path, UriKind.Absolute, out uri)) { _path = uri.GetLeftPart(UriPartial.Path); _params.Append(uri.Query); } else { // If the url is being built as part of a WebPages request, use the template stack to identify the current template's virtual path. _path = GetPageRelativePath(httpContext, path); int queryStringIndex = (_path ?? String.Empty).IndexOf('?'); if (queryStringIndex != -1) { _params.Append(_path.Substring(queryStringIndex)); _path = _path.Substring(0, queryStringIndex); } } if (parameters != null) { AddParam(parameters); } } internal static VirtualPathUtilityBase DefaultVirtualPathUtility { get { return _defaultVirtualPathUtility; } } public string Path { get { return _path; } } public string QueryString { get { return _params.ToString(); } } private VirtualPathUtilityBase VirtualPathUtility { get { return _virtualPathUtility ?? _defaultVirtualPathUtility; } } /// /// Factory method to create an UrlBuilder instance /// public static UrlBuilder Create(string path, object parameters = null) { return new UrlBuilder(path, parameters); } public UrlBuilder AddPath(string path) { _path = EnsureTrailingSlash(_path); if (!path.IsEmpty()) { _path += HttpUtility.UrlPathEncode(path.TrimStart('/')); } return this; } public UrlBuilder AddPath(params string[] pathTokens) { foreach (var token in pathTokens) { AddPath(token); } return this; } public UrlBuilder AddParam(string name, object value) { if (!String.IsNullOrEmpty(name)) { _params.Append(_params.Length == 0 ? '?' : '&'); _params.Append(HttpUtility.UrlEncode(name)) .Append('=') .Append(HttpUtility.UrlEncode(Convert.ToString(value, CultureInfo.InvariantCulture))); } return this; } public UrlBuilder AddParam(object values) { var dictionary = new RouteValueDictionary(values); foreach (var item in dictionary) { AddParam(item.Key, item.Value); } return this; } public override string ToString() { return _path + _params; } private static HttpContextBase GetHttpContext() { return HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null; } private static string EnsureTrailingSlash(string path) { if (!path.IsEmpty() && path[path.Length - 1] != '/') { path += '/'; } return path; } private string GetPageRelativePath(HttpContextBase httpContext, string path) { if (httpContext == null) { return path; } var templateFile = TemplateStack.GetCurrentTemplate(httpContext); if (templateFile != null) { var templateVirtualPath = templateFile.TemplateInfo.VirtualPath; if (path.IsEmpty()) { path = templateVirtualPath; } else { path = VirtualPathUtility.Combine(templateVirtualPath, path); } } return VirtualPathUtility.ToAbsolute(path ?? "~/"); } public static implicit operator string(UrlBuilder builder) { return builder.ToString(); } } } ================================================ FILE: src/Microsoft.Web.Helpers/Video.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Web; using System.Web.Routing; using System.Web.WebPages; using Microsoft.Internal.Web.Utils; using Resources; namespace Microsoft.Web.Helpers { public static class Video { private const string FlashCab = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"; private const string FlashClassId = "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"; private const string FlashMimeType = "application/x-shockwave-flash"; private const string MediaPlayerClassId = "clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6"; private const string MediaPlayerMimeType = "application/x-mplayer2"; private const string OleMimeType = "application/x-oleobject"; private const string SilverlightMimeType = "application/x-silverlight-2"; // These attributes can't be specified using anonymous objects (either because they are available as separate arguments or because // they don't make sense in the context of the helper). private static readonly string[] _globalBlacklist = new[] { "width", "height", "type", "data", "classid", "codebase" }; private static readonly string[] _mediaPlayerBlacklist = new[] { "autoStart", "playCount", "uiMode", "stretchToFit", "enableContextMenu", "mute", "volume", "baseURL" }; private static readonly string[] _silverlightBlacklist = new[] { "background", "initparams", "minruntimeversion", "autoUpgrade" }; private static readonly string[] _flashBlacklist = new[] { "play", "loop", "menu", "bgColor", "quality", "scale", "wmode", "base" }; private static VirtualPathUtilityWrapper _pathUtility = new VirtualPathUtilityWrapper(); #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif private static HttpContextBase HttpContext { get { var httpContext = System.Web.HttpContext.Current; return httpContext == null ? null : new HttpContextWrapper(httpContext); } } // see: http://kb2.adobe.com/cps/127/tn_12701.html #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "10#", Justification = "string parameter passed to flash in object tag")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "bgColor", Justification = "This method is public and the parameter name cannot be changed")] public static HelperResult Flash(string path, string width = null, string height = null, bool play = true, bool loop = true, bool menu = true, string bgColor = null, string quality = null, string scale = null, string windowMode = null, string baseUrl = null, string version = null, object options = null, object htmlAttributes = null, string embedName = null) { return Flash(HttpContext, _pathUtility, path, width, height, play, loop, menu, bgColor, quality, scale, windowMode, baseUrl, version, options, htmlAttributes, embedName); } // see: http://msdn.microsoft.com/en-us/library/aa392321 #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "10#", Justification = "string parameter passed to media player in object tag")] public static HelperResult MediaPlayer(string path, string width = null, string height = null, bool autoStart = true, int playCount = 1, string uiMode = null, bool stretchToFit = false, bool enableContextMenu = true, bool mute = false, int volume = -1, string baseUrl = null, object options = null, object htmlAttributes = null, string embedName = null) { return MediaPlayer(HttpContext, _pathUtility, path, width, height, autoStart, playCount, uiMode, stretchToFit, enableContextMenu, mute, volume, baseUrl, options, htmlAttributes, embedName); } // should users really use Silverlight.js? // see: http://msdn.microsoft.com/en-us/library/cc838259(v=VS.95).aspx #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "bgColor", Justification = "This method is public and the parameter name cannot be changed")] public static HelperResult Silverlight(string path, string width, string height, string bgColor = null, string initParameters = null, string minimumVersion = null, bool autoUpgrade = true, object options = null, object htmlAttributes = null) { return Silverlight(HttpContext, _pathUtility, path, width, height, bgColor, initParameters, minimumVersion, autoUpgrade, options, htmlAttributes); } internal static HelperResult Flash(HttpContextBase context, VirtualPathUtilityBase pathUtility, string path, string width = null, string height = null, bool play = true, bool loop = true, bool menu = true, string backgroundColor = null, string quality = null, string scale = null, string windowMode = null, string baseUrl = null, string version = null, object options = null, object htmlAttributes = null, string embedName = null) { var parameters = ObjectToDictionary(options, "options", _flashBlacklist); if (!play) { parameters["play"] = false; } if (!loop) { parameters["loop"] = false; } if (!menu) { parameters["menu"] = false; } if (!String.IsNullOrEmpty(backgroundColor)) { parameters["bgColor"] = backgroundColor; } if (!String.IsNullOrEmpty(quality)) { parameters["quality"] = quality; } if (!String.IsNullOrEmpty(scale)) { parameters["scale"] = scale; } if (!String.IsNullOrEmpty(windowMode)) { parameters["wmode"] = windowMode; } if (!String.IsNullOrEmpty(baseUrl)) { parameters["base"] = baseUrl; } string cab = FlashCab; if (!String.IsNullOrEmpty(version)) { cab += "#version=" + version.Replace('.', ','); } return GetHtml(context, pathUtility, path, width, height, OleMimeType, null, FlashClassId, cab, "movie", FlashMimeType, parameters, htmlAttributes, embedName); } internal static HelperResult MediaPlayer(HttpContextBase context, VirtualPathUtilityBase pathUtility, string path, string width = null, string height = null, bool autoStart = true, int playCount = 1, string uiMode = null, bool stretchToFit = false, bool enableContextMenu = true, bool mute = false, int volume = -1, string baseUrl = null, object options = null, object htmlAttributes = null, string embedName = null) { var parameters = ObjectToDictionary(options, "options", _mediaPlayerBlacklist); if (!autoStart) { parameters["autoStart"] = false; } if (playCount != 1) { parameters["playCount"] = playCount; } if (!String.IsNullOrEmpty(uiMode)) { parameters["uiMode"] = uiMode; } if (stretchToFit) { parameters["stretchToFit"] = true; } if (!enableContextMenu) { parameters["enableContextMenu"] = false; } if (mute) { parameters["mute"] = true; } if (volume >= 0) { parameters["volume"] = Math.Min(volume, 100); } if (!String.IsNullOrEmpty(baseUrl)) { parameters["baseURL"] = baseUrl; } return GetHtml(context, pathUtility, path, width, height, null, null, MediaPlayerClassId, null, "URL", MediaPlayerMimeType, parameters, htmlAttributes, embedName); } internal static HelperResult Silverlight(HttpContextBase context, VirtualPathUtilityBase pathUtility, string path, string width, string height, string backgroundColor = null, string initParameters = null, string minimumVersion = null, bool autoUpgrade = true, object options = null, object htmlAttributes = null) { if (String.IsNullOrEmpty(width)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "width"); } if (String.IsNullOrEmpty(height)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "height"); } var parameters = ObjectToDictionary(options, "options", _silverlightBlacklist); if (!String.IsNullOrEmpty(backgroundColor)) { parameters["background"] = backgroundColor; } if (!String.IsNullOrEmpty(initParameters)) { parameters["initparams"] = initParameters; } if (!String.IsNullOrEmpty(minimumVersion)) { parameters["minruntimeversion"] = minimumVersion; } if (!autoUpgrade) { parameters["autoUpgrade"] = autoUpgrade; } return GetHtml(context, pathUtility, path, width, height, SilverlightMimeType, "data:" + SilverlightMimeType + ",", // ',' required for Opera support null, null, "source", null, parameters, htmlAttributes, null, tw => { tw.WriteLine(""); tw.WriteLine("\"Get"); tw.WriteLine(""); }); } private static IDictionary ObjectToDictionary(object o, string argName, string[] blackList) { var dictionary = new RouteValueDictionary(o); foreach (var key in dictionary.Keys) { if (blackList.Contains(key, StringComparer.OrdinalIgnoreCase)) { throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, HelpersToolkitResources.Video_PropertyCannotBeSet, key), argName); } } return dictionary; } private static HelperResult GetHtml(HttpContextBase context, VirtualPathUtilityBase pathUtility, string path, string width, string height, string objectType, string objectDataType, string objectClassId, string objectCodeBase, string pathParamName, string embedContentType, IDictionary parameters = null, object htmlAttributes = null, string embedName = null, Action plugin = null) { path = ValidatePath(context, pathUtility, path); var objectAttr = ObjectToDictionary(htmlAttributes, "htmlAttributes", _globalBlacklist); objectAttr["width"] = width; objectAttr["height"] = height; objectAttr["type"] = objectType; objectAttr["data"] = objectDataType; objectAttr["classid"] = objectClassId; objectAttr["codebase"] = objectCodeBase; return new HelperResult(tw => { tw.Write(" a.Key, StringComparer.OrdinalIgnoreCase)) { var value = (a.Value == null) ? null : a.Value.ToString(); WriteIfNotNullOrEmpty(tw, a.Key, value); } tw.WriteLine(">"); // object parameters if (!String.IsNullOrEmpty(pathParamName)) { tw.WriteLine("", HttpUtility.HtmlAttributeEncode(pathParamName), HttpUtility.HtmlAttributeEncode(HttpUtility.UrlPathEncode(path))); } if (parameters != null) { foreach (var p in parameters) { tw.WriteLine("", HttpUtility.HtmlAttributeEncode(p.Key), HttpUtility.HtmlAttributeEncode(p.Value.ToString())); } } if (!String.IsNullOrEmpty(embedContentType)) { tw.Write(""); } if (plugin != null) { plugin(tw); } tw.WriteLine(""); }); } private static string ValidatePath(HttpContextBase context, VirtualPathUtilityBase pathUtility, string path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } string originalPath = path; if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { // resolve relative paths path = pathUtility.Combine(context.Request.AppRelativeCurrentExecutionFilePath, path); // resolve to app absolute - SL doesn't support app relative path = pathUtility.ToAbsolute(path); if (!File.Exists(context.Server.MapPath(path))) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, HelpersToolkitResources.Video_FileDoesNotExist, originalPath)); } } return path; } private static void WriteIfNotNullOrEmpty(TextWriter tw, string key, string value) { Debug.Assert(!String.IsNullOrEmpty(key)); if (!String.IsNullOrEmpty(value)) { tw.Write("{0}=\"{1}\" ", HttpUtility.HtmlEncode(key), HttpUtility.HtmlAttributeEncode(value)); } } } } ================================================ FILE: src/Microsoft.Web.Helpers/VirtualPathUtilityBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Helpers { public abstract class VirtualPathUtilityBase { public abstract string Combine(string basePath, string relativePath); public abstract string ToAbsolute(string virtualPath); } } ================================================ FILE: src/Microsoft.Web.Helpers/VirtualPathUtilityWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web; namespace Microsoft.Web.Helpers { internal sealed class VirtualPathUtilityWrapper : VirtualPathUtilityBase { public override string Combine(string basePath, string relativePath) { return VirtualPathUtility.Combine(basePath, relativePath); } public override string ToAbsolute(string virtualPath) { return VirtualPathUtility.ToAbsolute(virtualPath); } } } ================================================ FILE: src/Microsoft.Web.Helpers/packages.config ================================================  ================================================ FILE: src/Microsoft.Web.Mvc/AcceptAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class AcceptAttribute : DataTypeAttribute, IClientValidatable { public AcceptAttribute() : base("upload") { ErrorMessage = MvcResources.FileExtensionsAttribute_Invalid; ErrorMessage = MvcResources.AcceptAttribute_Invalid; } public string MimeTypes { get; set; } private string MimeTypesFormatted { get { return MimeTypesParsed.Aggregate((left, right) => left + ", " + right); } } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")] private string MimeTypesNormalized { get { return MimeTypes.Replace(" ", String.Empty).ToLowerInvariant(); } } private IEnumerable MimeTypesParsed { get { return MimeTypesNormalized.Split(','); } } public override string FormatErrorMessage(string name) { return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, MimeTypesFormatted); } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule { ValidationType = "accept", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; rule.ValidationParameters["mimetype"] = MimeTypesNormalized; yield return rule; } public override bool IsValid(object value) { if (value == null) { return true; } HttpPostedFileBase valueAsFileBase = value as HttpPostedFileBase; if (valueAsFileBase != null) { return ValidateMimeTypes(valueAsFileBase.ContentType); } string valueAsString = value as string; if (valueAsString != null) { return ValidateMimeTypes(valueAsString); } return false; } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")] private bool ValidateMimeTypes(string mimeType) { return MimeTypesParsed.Contains(mimeType.ToLowerInvariant()); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ActionLinkAreaAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public sealed class ActionLinkAreaAttribute : Attribute { public ActionLinkAreaAttribute(string area) { if (area == null) { throw new ArgumentNullException("area"); } Area = area; } public string Area { get; private set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/AjaxOnlyAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using System.Web.Mvc; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class AjaxOnlyAttribute : ActionMethodSelectorAttribute { public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } // Dev10 #939671 - If this attribute is going to say AJAX *only*, then we need to check the header // specifically, as otherwise clients can modify the form or query string to contain the name/value // pair we're looking for. return (controllerContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest"); } } } ================================================ FILE: src/Microsoft.Web.Mvc/AreaHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.Routing; namespace Microsoft.Web.Mvc { public static class AreaHelpers { public static string GetAreaName(RouteBase route) { IRouteWithArea routeWithArea = route as IRouteWithArea; if (routeWithArea != null) { return routeWithArea.Area; } Route castRoute = route as Route; if (castRoute != null && castRoute.DataTokens != null) { return castRoute.DataTokens["area"] as string; } return null; } public static string GetAreaName(RouteData routeData) { object area; if (routeData.DataTokens.TryGetValue("area", out area)) { return area as string; } return GetAreaName(routeData.Route); } } } ================================================ FILE: src/Microsoft.Web.Mvc/AsyncManagerExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc.Async; namespace Microsoft.Web.Mvc { public static class AsyncManagerExtensions { [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "An unhandled exception here will bring down the worker process.")] [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1409:RemoveUnnecessaryCode", Justification = "The empty lock statement is required for synchronization.")] public static void RegisterTask(this AsyncManager asyncManager, Func beginDelegate, Action endDelegate) { if (asyncManager == null) { throw new ArgumentNullException("asyncManager"); } if (beginDelegate == null) { throw new ArgumentNullException("beginDelegate"); } if (endDelegate == null) { throw new ArgumentNullException("endDelegate"); } // need to wait to execute the callback until after BeginXxx() has completed object delegateExecutingLockObj = new object(); AsyncCallback callback = ar => { lock (delegateExecutingLockObj) { // this empty lock is required to synchronized with the beginDelegate call } if (!ar.CompletedSynchronously) { try { asyncManager.Sync(() => endDelegate(ar)); // called on different thread, so have to take application lock } catch { // Need to swallow exceptions, as otherwise unhandled exceptions on a ThreadPool thread // can bring down the entire worker process. } finally { asyncManager.OutstandingOperations.Decrement(); } } }; IAsyncResult asyncResult; asyncManager.OutstandingOperations.Increment(); try { lock (delegateExecutingLockObj) { asyncResult = beginDelegate(callback); } } catch { asyncManager.OutstandingOperations.Decrement(); throw; } if (asyncResult.CompletedSynchronously) { try { endDelegate(asyncResult); // call on same thread } finally { asyncManager.OutstandingOperations.Decrement(); } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ButtonBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public static class ButtonBuilder { public static TagBuilder SubmitButton(string name, string buttonText, IDictionary htmlAttributes) { TagBuilder buttonTag = new TagBuilder("input"); buttonTag.MergeAttribute("type", "submit"); if (!buttonTag.Attributes.ContainsKey("id") && name != null) { buttonTag.GenerateId(name); } if (!String.IsNullOrEmpty(name)) { buttonTag.MergeAttribute("name", name); } if (!String.IsNullOrEmpty(buttonText)) { buttonTag.MergeAttribute("value", buttonText); } buttonTag.MergeAttributes(htmlAttributes, true); return buttonTag; } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static TagBuilder SubmitImage(string name, string sourceUrl, IDictionary htmlAttributes) { TagBuilder buttonTag = new TagBuilder("input"); buttonTag.MergeAttribute("type", "image"); if (!buttonTag.Attributes.ContainsKey("id")) { buttonTag.GenerateId(name); } if (!String.IsNullOrEmpty(name)) { buttonTag.MergeAttribute("name", name); } if (!String.IsNullOrEmpty(sourceUrl)) { buttonTag.MergeAttribute("src", sourceUrl); } buttonTag.MergeAttributes(htmlAttributes, true); return buttonTag; } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "This conversion is appropriate because it's for HTML, not for comparsion normalization")] public static TagBuilder Button(string name, string buttonText, HtmlButtonType type, string onClickMethod, IDictionary htmlAttributes) { if (name == null) { throw new ArgumentNullException("name"); } TagBuilder buttonTag = new TagBuilder("button"); if (!String.IsNullOrEmpty(name)) { buttonTag.MergeAttribute("name", name); } buttonTag.MergeAttribute("type", type.ToString().ToLowerInvariant()); buttonTag.InnerHtml = buttonText; if (!String.IsNullOrEmpty(onClickMethod)) { buttonTag.MergeAttribute("onclick", onClickMethod); } buttonTag.MergeAttributes(htmlAttributes, true); return buttonTag; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ButtonsAndLinkExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public static class ButtonsAndLinkExtensions { /// /// Creates a submit button for your form /// /// The helper which we extend. /// The name. /// public static MvcHtmlString SubmitButton(this HtmlHelper helper, string name) { return SubmitButton(helper, name, null, null); } /// /// Creates a submit button for your form /// /// The helper which we extend. /// The name. /// The text for the button face /// public static MvcHtmlString SubmitButton(this HtmlHelper helper, string name, string buttonText) { return SubmitButton(helper, name, buttonText, null); } /// /// Creates a submit button for your form /// /// The helper which we extend. public static MvcHtmlString SubmitButton(this HtmlHelper helper) { return SubmitButton(helper, null, null, null); } /// /// Creates a submit button for your form /// /// The helper which we extend. /// Name of the button /// The text for the button face /// Any attributes you want set on the tag. Use anonymous-type declaration for this: new{class=cssclass} /// public static MvcHtmlString SubmitButton(this HtmlHelper helper, string name, string buttonText, object htmlAttributes) { return helper.SubmitButton(name, buttonText, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } /// /// Creates a submit button for your form /// /// The helper which we extend. /// Name of the button /// The text for the button face /// Dictionary of HTML settings /// [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "Required for Extension Method")] public static MvcHtmlString SubmitButton(this HtmlHelper helper, string name, string buttonText, IDictionary htmlAttributes) { return MvcHtmlString.Create(ButtonBuilder.SubmitButton(name, buttonText, htmlAttributes).ToString(TagRenderMode.SelfClosing)); } /// /// Creates a submit button for your form using an image /// /// The helper which we extend. /// Name of the button /// The URL for the image /// public static MvcHtmlString SubmitImage(this HtmlHelper helper, string name, string imageSrc) { return helper.SubmitImage(name, imageSrc, null); } /// /// Creates a submit button for your form using an image /// /// The helper which we extend. /// Name of the button /// The URL for the image /// Any attributes you want set on the tag. Use anonymous-type declaration for this: new{class=cssclass} /// public static MvcHtmlString SubmitImage(this HtmlHelper helper, string name, string imageSrc, object htmlAttributes) { return helper.SubmitImage(name, imageSrc, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } /// /// Creates a submit button for your form using an image /// /// The helper which we extend. /// Name of the button /// The URL for the image /// Dictionary of HTML settings /// public static MvcHtmlString SubmitImage(this HtmlHelper helper, string name, string imageSrc, IDictionary htmlAttributes) { if (imageSrc == null) { throw new ArgumentNullException("imageSrc"); } string resolvedUrl = UrlHelper.GenerateContentUrl(imageSrc, helper.ViewContext.HttpContext); return MvcHtmlString.Create(ButtonBuilder.SubmitImage(name, resolvedUrl, htmlAttributes).ToString(TagRenderMode.SelfClosing)); } /// /// A Simple button you can use with javascript /// /// The helper which we extend. /// Name of the button /// The text for the button face /// The button type (Button, Submit, or Reset) /// public static MvcHtmlString Button(this HtmlHelper helper, string name, string buttonText, HtmlButtonType buttonType) { return helper.Button(name, buttonText, buttonType, null, (IDictionary)null); } /// /// A Simple button you can use with javascript /// /// The helper which we extend. /// Name of the button /// The text for the button face /// The button type (Button, Submit, or Reset) /// The method or script routine to call when the button is clicked. /// public static MvcHtmlString Button(this HtmlHelper helper, string name, string buttonText, HtmlButtonType buttonType, string onClickMethod) { return helper.Button(name, buttonText, buttonType, onClickMethod, (IDictionary)null); } /// /// A Simple button you can use with javascript /// /// The helper which we extend. /// Name of the button /// The text for the button face /// The button type (Button, Submit, or Reset) /// The method or script routine to call when the button is clicked. /// Any attributes you want set on the tag. Use anonymous-type declaration for this: new{class=cssclass} /// public static MvcHtmlString Button(this HtmlHelper helper, string name, string buttonText, HtmlButtonType buttonType, string onClickMethod, object htmlAttributes) { return helper.Button(name, buttonText, buttonType, onClickMethod, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } /// /// A Simple button you can use with javascript /// /// The helper which we extend. /// Name of the button /// The text for the button face /// The button type (Button, Submit, or Reset) /// The method or script routine to call when the button is clicked. /// Any attributes you want set on the tag. Use anonymous-type declaration for this: new{class=cssclass} /// [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper", Justification = "This is an Extension Method and requires this argument")] public static MvcHtmlString Button(this HtmlHelper helper, string name, string buttonText, HtmlButtonType buttonType, string onClickMethod, IDictionary htmlAttributes) { return MvcHtmlString.Create(ButtonBuilder.Button(name, buttonText, buttonType, onClickMethod, htmlAttributes).ToString(TagRenderMode.Normal)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/CachedExpressionCompiler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Microsoft.Web.Mvc { // The caching expression tree compiler was copied from MVC core to MVC Futures so that Futures code could benefit // from it and so that it could be exposed as a public API. This is the only public entry point into the system. // See the comments in the ExpressionUtil namespace for more information. // // The unit tests for the ExpressionUtil.* types are in the System.Web.Mvc.Test project. public static class CachedExpressionCompiler { private static readonly ParameterExpression _unusedParameterExpr = Expression.Parameter(typeof(object), "_unused"); // Implements caching around LambdaExpression.Compile() so that equivalent expression trees only have to be // compiled once. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static Func Compile(this Expression> lambdaExpression) { if (lambdaExpression == null) { throw new ArgumentNullException("lambdaExpression"); } return ExpressionUtil.CachedExpressionCompiler.Process(lambdaExpression); } // Evaluates an expression (not a LambdaExpression), e.g. 2 + 2. public static object Evaluate(Expression arg) { if (arg == null) { throw new ArgumentNullException("arg"); } Func func = Wrap(arg); return func(null); } private static Func Wrap(Expression arg) { Expression> lambdaExpr = Expression.Lambda>(Expression.Convert(arg, typeof(object)), _unusedParameterExpr); return ExpressionUtil.CachedExpressionCompiler.Process(lambdaExpr); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ContentTypeAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public sealed class ContentTypeAttribute : ActionFilterAttribute { public ContentTypeAttribute(string contentType) { if (String.IsNullOrEmpty(contentType)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType"); } ContentType = contentType; } public string ContentType { get; private set; } public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.ContentType = ContentType; } public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.ContentType = ContentType; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ControllerExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Routing; using ExpressionHelper = Microsoft.Web.Mvc.Internal.ExpressionHelper; namespace Microsoft.Web.Mvc { public static class ControllerExtensions { // Shortcut to allow users to write this.RedirectToAction(x => x.OtherMethod()) to redirect // to a different method on the same controller. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static RedirectToRouteResult RedirectToAction(this TController controller, Expression> action) where TController : Controller { return RedirectToAction((Controller)controller, action); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static RedirectToRouteResult RedirectToAction(this Controller controller, Expression> action) where TController : Controller { if (controller == null) { throw new ArgumentNullException("controller"); } RouteValueDictionary routeValues = ExpressionHelper.GetRouteValuesFromExpression(action); return new RedirectToRouteResult(routeValues); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/ActionLink.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.Web.Mvc; using System.Web.Routing; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls { [ParseChildren(true)] [PersistChildren(false)] public class ActionLink : MvcControl { private string _actionName; private string _controllerName; private string _text; private string _routeName; private RouteValues _values; [DefaultValue("")] public string ActionName { get { return _actionName ?? String.Empty; } set { _actionName = value; } } [DefaultValue("")] public string ControllerName { get { return _controllerName ?? String.Empty; } set { _controllerName = value; } } [DefaultValue("")] public string RouteName { get { return _routeName ?? String.Empty; } set { _routeName = value; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [PersistenceMode(PersistenceMode.InnerProperty)] public RouteValues Values { get { if (_values == null) { _values = new RouteValues(); } return _values; } } public string Text { get { return _text ?? String.Empty; } set { _text = value; } } protected override void Render(HtmlTextWriter writer) { RouteValueDictionary routeValues = new RouteValueDictionary(); foreach (var attribute in Values.Attributes) { routeValues.Add(attribute.Key, attribute.Value); } if (!String.IsNullOrEmpty(ActionName) && !routeValues.ContainsKey("action")) { routeValues.Add("action", ActionName); } if (!String.IsNullOrEmpty(ControllerName) && !routeValues.ContainsKey("controller")) { routeValues.Add("controller", ControllerName); } string href = null; if (DesignMode) { href = "/"; } else { VirtualPathData vpd = RouteTable.Routes.GetVirtualPathForArea(ViewContext.RequestContext, RouteName, routeValues); if (vpd == null) { throw new InvalidOperationException("A route that matches the requested values could not be located in the route table."); } href = vpd.VirtualPath; } foreach (var attribute in Attributes) { writer.AddAttribute(attribute.Key, attribute.Value); } if (!Attributes.ContainsKey("href")) { writer.AddAttribute(HtmlTextWriterAttribute.Href, href); } writer.RenderBeginTag(HtmlTextWriterTag.A); writer.WriteEncodedText(Text); writer.RenderEndTag(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/DropDownList.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Web.Mvc; using System.Web.UI; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Controls { // TODO: Have ListBoxBase class to use with DropDownList and ListBox? // TODO: Do we need a way to explicitly specify the items? And only get the selected value(s) from ViewData? public class DropDownList : MvcControl { private string _name; private string _optionLabel; [DefaultValue("")] public string Name { get { return _name ?? String.Empty; } set { _name = value; } } [DefaultValue("")] public string OptionLabel { get { return _optionLabel ?? String.Empty; } set { _optionLabel = value; } } private object GetModelStateValue(string key, Type destinationType) { ModelState modelState; if (ViewData.ModelState.TryGetValue(key, out modelState)) { return modelState.Value.ConvertTo(destinationType, null /* culture */); } return null; } private IEnumerable GetSelectData(string name) { object o = null; if (ViewData != null) { o = ViewData.Eval(name); } if (o == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.HtmlHelper_MissingSelectData, name, "IEnumerable")); } IEnumerable selectList = o as IEnumerable; if (selectList == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.HtmlHelper_WrongSelectDataType, name, o.GetType().FullName, "IEnumerable")); } return selectList; } protected override void Render(HtmlTextWriter writer) { if (!DesignMode && String.IsNullOrEmpty(Name)) { throw new InvalidOperationException(MvcResources.CommonControls_NameRequired); } if (DesignMode) { RenderDesignMode(writer); } else { RenderRuntime(writer); } } private void RenderDesignMode(HtmlTextWriter writer) { writer.RenderBeginTag(HtmlTextWriterTag.Select); writer.RenderBeginTag(HtmlTextWriterTag.Option); if (String.IsNullOrEmpty(OptionLabel)) { writer.WriteEncodedText(MvcResources.DropDownList_SampleItem); } else { writer.WriteEncodedText(OptionLabel); } writer.RenderEndTag(); writer.RenderEndTag(); } private void RenderRuntime(HtmlTextWriter writer) { // TODO: Move this to the base class once it exists bool allowMultiple = false; SortedDictionary attrs = new SortedDictionary(); foreach (KeyValuePair attribute in Attributes) { attrs.Add(attribute.Key, attribute.Value); } attrs.Add("name", Name); if (!String.IsNullOrEmpty(ID)) { attrs.Add("id", ID); } if (allowMultiple) { attrs.Add("multiple", "multiple"); } // If there are any errors for a named field, we add the css attribute. ModelState modelState; if (ViewData.ModelState.TryGetValue(Name, out modelState)) { if (modelState.Errors.Count > 0) { string currentValue; if (attrs.TryGetValue("class", out currentValue)) { attrs["class"] = HtmlHelper.ValidationInputCssClassName + " " + currentValue; } else { attrs["class"] = HtmlHelper.ValidationInputCssClassName; } } } foreach (KeyValuePair attribute in attrs) { writer.AddAttribute(attribute.Key, Convert.ToString(attribute.Value, CultureInfo.CurrentCulture)); } writer.RenderBeginTag(HtmlTextWriterTag.Select); // Use ViewData to get the list of items IEnumerable selectList = GetSelectData(Name); object defaultValue = (allowMultiple) ? GetModelStateValue(Name, typeof(string[])) : GetModelStateValue(Name, typeof(string)); if (defaultValue != null) { IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue }; IEnumerable values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); HashSet selectedValues = new HashSet(values, StringComparer.OrdinalIgnoreCase); List newSelectList = new List(); foreach (SelectListItem item in selectList) { item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); newSelectList.Add(item); } selectList = newSelectList; } // Render the option label if it exists if (!String.IsNullOrEmpty(OptionLabel)) { writer.AddAttribute(HtmlTextWriterAttribute.Value, String.Empty); writer.RenderBeginTag(HtmlTextWriterTag.Option); writer.WriteEncodedText(OptionLabel); writer.RenderEndTag(); } // Render out the list items foreach (SelectListItem listItem in selectList) { if (listItem.Value != null) { writer.AddAttribute(HtmlTextWriterAttribute.Value, listItem.Value); } if (listItem.Selected) { writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected"); } writer.RenderBeginTag(HtmlTextWriterTag.Option); writer.WriteEncodedText(listItem.Text); writer.RenderEndTag(); } writer.RenderEndTag(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/EncodeType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Controls { public enum EncodeType { Html, HtmlAttribute, None, } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/Hidden.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Controls { public class Hidden : MvcInputControl { public Hidden() : base("hidden") { } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/Label.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.Globalization; using System.Web; using System.Web.UI; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Controls { public class Label : MvcControl { private string _format; private string _name; private int _truncateLength = -1; private string _truncateText = "..."; [DefaultValue(EncodeType.Html)] public EncodeType EncodeType { get; set; } [DefaultValue("")] public string Format { get { return _format ?? String.Empty; } set { _format = value; } } [DefaultValue("")] public string Name { get { return _name ?? String.Empty; } set { _name = value; } } [DefaultValue(-1)] [Description("The length of the text at which to truncate the value. Set to -1 to never truncate.")] public int TruncateLength { get { return _truncateLength; } set { if (value < -1) { throw new ArgumentOutOfRangeException("value", "The TruncateLength property must be greater than or equal to -1."); } _truncateLength = value; } } [DefaultValue("...")] [Description("The text to display at the end of the string if it is truncated. This text is never encoded.")] public string TruncateText { get { return _truncateText ?? String.Empty; } set { _truncateText = value; } } protected override void Render(HtmlTextWriter writer) { if (!DesignMode && String.IsNullOrEmpty(Name)) { throw new InvalidOperationException(MvcResources.CommonControls_NameRequired); } string stringValue = String.Empty; if (ViewData != null) { object rawValue = ViewData.Eval(Name); if (String.IsNullOrEmpty(Format)) { stringValue = Convert.ToString(rawValue, CultureInfo.CurrentCulture); } else { stringValue = String.Format(CultureInfo.CurrentCulture, Format, rawValue); } } writer.AddAttribute(HtmlTextWriterAttribute.Name, Name); if (!String.IsNullOrEmpty(ID)) { writer.AddAttribute(HtmlTextWriterAttribute.Id, ID); } bool wasTruncated = false; if ((TruncateLength >= 0) && (stringValue.Length > TruncateLength)) { stringValue = stringValue.Substring(0, TruncateLength); wasTruncated = true; } switch (EncodeType) { case EncodeType.Html: writer.Write(HttpUtility.HtmlEncode(stringValue)); break; case EncodeType.HtmlAttribute: writer.Write(HttpUtility.HtmlAttributeEncode(stringValue)); break; case EncodeType.None: writer.Write(stringValue); break; } if (wasTruncated) { writer.Write(TruncateText); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/MvcControl.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Web.Mvc; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls { // TODO: Consider using custom HTML writer instead of the default one to get prettier rendering public abstract class MvcControl : Control, IAttributeAccessor { private IDictionary _attributes; private IViewDataContainer _viewDataContainer; private ViewContext _viewContext; [Browsable(false)] public IDictionary Attributes { get { EnsureAttributes(); return _attributes; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override bool EnableViewState { get { return base.EnableViewState; } set { base.EnableViewState = value; } } public ViewContext ViewContext { get { if (_viewContext == null) { // TODO: Is this logic correct? Why not just case Page to ViewPage? Control parent = Parent; while (parent != null) { ViewPage viewPage = parent as ViewPage; if (viewPage != null) { _viewContext = viewPage.ViewContext; break; } parent = parent.Parent; } } return _viewContext; } } public IViewDataContainer ViewDataContainer { get { if (_viewDataContainer == null) { Control parent = Parent; while (parent != null) { _viewDataContainer = parent as IViewDataContainer; if (_viewDataContainer != null) { break; } parent = parent.Parent; } } return _viewDataContainer; } } public ViewDataDictionary ViewData { get { IViewDataContainer vdc = ViewDataContainer; return (vdc == null) ? null : vdc.ViewData; } } private void EnsureAttributes() { if (_attributes == null) { _attributes = new SortedDictionary(StringComparer.OrdinalIgnoreCase); } } protected virtual string GetAttribute(string key) { EnsureAttributes(); string value; _attributes.TryGetValue(key, out value); return value; } protected virtual void SetAttribute(string key, string value) { EnsureAttributes(); _attributes[key] = value; } #region IAttributeAccessor Members string IAttributeAccessor.GetAttribute(string key) { return GetAttribute(key); } void IAttributeAccessor.SetAttribute(string key, string value) { SetAttribute(key, value); } #endregion } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/MvcInputControl.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Web.Mvc; using System.Web.UI; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Controls { public abstract class MvcInputControl : MvcControl { private string _format; private string _name; protected MvcInputControl(string inputType) { if (String.IsNullOrEmpty(inputType)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "inputType"); } InputType = inputType; } [DefaultValue("")] public string Format { get { return _format ?? String.Empty; } set { _format = value; } } [Browsable(false)] public string InputType { get; private set; } [DefaultValue("")] public string Name { get { return _name ?? String.Empty; } set { _name = value; } } private ModelState GetModelState() { return ViewData.ModelState[Name]; } private object GetModelStateValue(Type destinationType) { ModelState modelState = GetModelState(); if (modelState != null) { return modelState.Value.ConvertTo(destinationType, null /* culture */); } return null; } protected override void Render(HtmlTextWriter writer) { if (!DesignMode && String.IsNullOrEmpty(Name)) { throw new InvalidOperationException(MvcResources.CommonControls_NameRequired); } SortedDictionary attrs = new SortedDictionary(); foreach (KeyValuePair attribute in Attributes) { attrs.Add(attribute.Key, attribute.Value); } if (!Attributes.ContainsKey("type")) { attrs.Add("type", InputType); } attrs.Add("name", Name); if (!String.IsNullOrEmpty(ID)) { attrs.Add("id", ID); } if (DesignMode) { // Use a dummy value in design mode attrs.Add("value", "TextBox"); } else { string attemptedValue = (string)GetModelStateValue(typeof(string)); if (attemptedValue != null) { // Never format the attempted value since it was already formatted in the previous request attrs.Add("value", attemptedValue); } else { // Use an explicit value attribute if it is available. Otherwise get it from ViewData. string attributeValue; Attributes.TryGetValue("value", out attributeValue); object rawValue = attributeValue ?? ViewData.Eval(Name); string stringValue; if (String.IsNullOrEmpty(Format)) { stringValue = Convert.ToString(rawValue, CultureInfo.CurrentCulture); } else { stringValue = String.Format(CultureInfo.CurrentCulture, Format, rawValue); } // The HtmlTextWriter will automatically encode this value attrs.Add("value", stringValue); } // If there are any errors for a named field, we add the CSS attribute. ModelState modelState = GetModelState(); if ((modelState != null) && (modelState.Errors.Count > 0)) { string currentValue; if (attrs.TryGetValue("class", out currentValue)) { attrs["class"] = HtmlHelper.ValidationInputCssClassName + " " + currentValue; } else { attrs["class"] = HtmlHelper.ValidationInputCssClassName; } } } foreach (KeyValuePair attribute in attrs) { writer.AddAttribute(attribute.Key, Convert.ToString(attribute.Value, CultureInfo.CurrentCulture)); } writer.RenderBeginTag(HtmlTextWriterTag.Input); writer.RenderEndTag(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/Password.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Controls { public class Password : MvcInputControl { public Password() : base("password") { } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/Repeater.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls { [ParseChildren(true)] [PersistChildren(false)] public class Repeater : MvcControl { private string _name; [DefaultValue(null)] [Browsable(false)] [PersistenceMode(PersistenceMode.InnerProperty)] [TemplateContainer(typeof(RepeaterItem))] [TemplateInstance(TemplateInstance.Multiple)] public ITemplate ItemTemplate { get; set; } [DefaultValue(null)] [Browsable(false)] [PersistenceMode(PersistenceMode.InnerProperty)] [TemplateContainer(typeof(RepeaterItem))] [TemplateInstance(TemplateInstance.Single)] public ITemplate EmptyDataTemplate { get; set; } [DefaultValue("")] public string Name { get { return _name ?? String.Empty; } set { _name = value; } } [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The child objects are disposed when their container is disposed")] protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); // Dummy control to which we parent all the data item controls Control containerControl = new Control(); IEnumerable dataItems = ViewData.Eval(Name) as IEnumerable; bool hasData = false; if (dataItems != null) { int index = 0; foreach (object dataItem in dataItems) { hasData = true; RepeaterItem repeaterItem = new RepeaterItem(index, dataItem) { ViewData = new ViewDataDictionary(dataItem), }; ItemTemplate.InstantiateIn(repeaterItem); containerControl.Controls.Add(repeaterItem); index++; } } if (!hasData) { // If there was no data, instantiate the EmptyDataTemplate Control emptyDataContainer = new Control(); EmptyDataTemplate.InstantiateIn(emptyDataContainer); containerControl.Controls.Add(emptyDataContainer); } Controls.Add(containerControl); containerControl.DataBind(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/RepeaterItem.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls { public class RepeaterItem : Control, IDataItemContainer, IViewDataContainer { private object _dataItem; private int _itemIndex; public RepeaterItem(int itemIndex, object dataItem) { _itemIndex = itemIndex; _dataItem = dataItem; } public object DataItem { get { return _dataItem; } } public int DataItemIndex { get { return _itemIndex; } } public int DisplayIndex { get { return _itemIndex; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is intended to be settable for unit testing purposes.")] public ViewDataDictionary ViewData { get; set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/RouteValues.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls { public class RouteValues : IAttributeAccessor { private IDictionary _attributes; public IDictionary Attributes { get { EnsureAttributes(); return _attributes; } } private void EnsureAttributes() { if (_attributes == null) { _attributes = new SortedDictionary(StringComparer.OrdinalIgnoreCase); } } protected virtual string GetAttribute(string key) { EnsureAttributes(); string value; _attributes.TryGetValue(key, out value); return value; } protected virtual void SetAttribute(string key, string value) { EnsureAttributes(); _attributes[key] = value; } #region IAttributeAccessor Members string IAttributeAccessor.GetAttribute(string key) { return GetAttribute(key); } void IAttributeAccessor.SetAttribute(string key, string value) { SetAttribute(key, value); } #endregion } } ================================================ FILE: src/Microsoft.Web.Mvc/Controls/TextBox.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Controls { public class TextBox : MvcInputControl { public TextBox() : base("text") { } } } ================================================ FILE: src/Microsoft.Web.Mvc/CookieValueProviderFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.Web; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class CookieValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { HttpCookieCollection cookies = controllerContext.HttpContext.Request.Cookies; Dictionary backingStore = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < cookies.Count; i++) { HttpCookie cookie = cookies[i]; if (!String.IsNullOrEmpty(cookie.Name)) { backingStore[cookie.Name] = cookie.Value; } } return new DictionaryValueProvider(backingStore, CultureInfo.InvariantCulture); } } } ================================================ FILE: src/Microsoft.Web.Mvc/CopyAsyncParametersAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using System.Web.Mvc.Async; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public sealed class CopyAsyncParametersAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } IAsyncManagerContainer container = filterContext.Controller as IAsyncManagerContainer; if (container != null) { AsyncManager asyncManager = container.AsyncManager; foreach (var entry in filterContext.ActionParameters) { asyncManager.Parameters[entry.Key] = entry.Value; } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/CreditCardAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class CreditCardAttribute : DataTypeAttribute, IClientValidatable { public CreditCardAttribute() : base("creditcard") { ErrorMessage = MvcResources.CreditCardAttribute_Invalid; } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ValidationType = "creditcard", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; } public override bool IsValid(object value) { if (value == null) { return true; } string creditCardNumber = value as string; if (creditCardNumber == null) { return false; } creditCardNumber = creditCardNumber.Replace("-", String.Empty); int checksum = 0; bool evenDigit = false; // http://www.beachnet.com/~hstiles/cardtype.html foreach (char digit in creditCardNumber.Reverse()) { if (!Char.IsDigit(digit)) { return false; } int digitValue = (digit - '0') * (evenDigit ? 2 : 1); evenDigit = !evenDigit; while (digitValue > 0) { checksum += digitValue % 10; digitValue /= 10; } } return (checksum % 10) == 0; } } } ================================================ FILE: src/Microsoft.Web.Mvc/CssExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public static class CssExtensions { public static MvcHtmlString Css(this HtmlHelper helper, string file) { return Css(helper, file, null); } public static MvcHtmlString Css(this HtmlHelper helper, string file, string mediaType) { if (String.IsNullOrEmpty(file)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "file"); } string src; if (ScriptExtensions.IsRelativeToDefaultPath(file)) { src = "~/Content/" + file; } else { src = file; } TagBuilder linkTag = new TagBuilder("link"); linkTag.MergeAttribute("type", "text/css"); linkTag.MergeAttribute("rel", "stylesheet"); if (mediaType != null) { linkTag.MergeAttribute("media", mediaType); } linkTag.MergeAttribute("href", UrlHelper.GenerateContentUrl(src, helper.ViewContext.HttpContext)); return MvcHtmlString.Create(linkTag.ToString(TagRenderMode.SelfClosing)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/DeserializeAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public sealed class DeserializeAttribute : CustomModelBinderAttribute { public DeserializeAttribute() { } internal MvcSerializer Serializer { get; set; } public override IModelBinder GetBinder() { return new DeserializingModelBinder(Serializer); } private sealed class DeserializingModelBinder : IModelBinder { private readonly MvcSerializer _serializer; public DeserializingModelBinder(MvcSerializer serializer) { _serializer = serializer ?? new MvcSerializer(); } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)", Justification = "The target object should make the correct culture determination, not this method.")] public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == null) { // nothing found return null; } string serializedValue = (string)valueProviderResult.ConvertTo(typeof(string)); return _serializer.Deserialize(serializedValue); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/DynamicReflectionObject.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Dynamic; using System.Globalization; using System.Linq; using System.Reflection; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { internal class DynamicReflectionObject : DynamicObject { private readonly object _realObject; private DynamicReflectionObject(object realObject) { _realObject = realObject; } public override bool TryGetMember(GetMemberBinder binder, out object result) { PropertyInfo propInfo = _realObject.GetType().GetProperty(binder.Name); if (propInfo == null) { PropertyInfo[] properties = _realObject.GetType().GetProperties(); if (properties.Length == 0) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, MvcResources.DynamicViewPage_NoProperties, binder.Name)); } string propNames = properties.Select(p => p.Name) .OrderBy(name => name) .Aggregate((left, right) => left + ", " + right); throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, MvcResources.DynamicViewPage_PropertyDoesNotExist, binder.Name, propNames)); } result = Wrap(propInfo.GetValue(_realObject, null)); return true; } public static dynamic Wrap(object obj) { // We really only want to wrap anonymous objects, but there's no surefire way to determine // that an object is anonymous. We'll use the best metrics we can (internal non-nested type // that derives directly from Object). if (obj != null) { Type type = obj.GetType(); if (!type.IsPublic && type.BaseType == typeof(Object) && type.DeclaringType == null) { return new DynamicReflectionObject(obj); } } return obj; } } } ================================================ FILE: src/Microsoft.Web.Mvc/DynamicViewDataDictionary.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Dynamic; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { internal class DynamicViewDataDictionary : DynamicObject { private readonly ViewDataDictionary _dictionary; private DynamicViewDataDictionary(ViewDataDictionary dictionary) { _dictionary = dictionary; } public object Model { get { return _dictionary.Model; } set { _dictionary.Model = value; } } public ModelMetadata ModelMetadata { get { return _dictionary.ModelMetadata; } set { _dictionary.ModelMetadata = value; } } public ModelStateDictionary ModelState { get { return _dictionary.ModelState; } } public TemplateInfo TemplateInfo { get { return _dictionary.TemplateInfo; } set { _dictionary.TemplateInfo = value; } } private bool GetValue(string name, out object result) { result = DynamicReflectionObject.Wrap(_dictionary.Eval(name)) ?? String.Empty; return true; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes.Length != 1) { throw new ArgumentException(MvcResources.DynamicViewDataDictionary_SingleIndexerOnly); } string name = indexes[0] as string; if (name == null) { throw new ArgumentException(MvcResources.DynamicViewDataDictionary_StringIndexerOnly); } return GetValue(name, out result); } public override bool TryGetMember(GetMemberBinder binder, out object result) { return GetValue(binder.Name, out result); } public static dynamic Wrap(ViewDataDictionary dictionary) { return new DynamicViewDataDictionary(dictionary); } } } ================================================ FILE: src/Microsoft.Web.Mvc/DynamicViewPage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class DynamicViewPage : ViewPage { public new dynamic Model { get { return DynamicReflectionObject.Wrap(base.Model); } } public new dynamic ViewData { get { return DynamicViewDataDictionary.Wrap(base.ViewData); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/DynamicViewPageOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class DynamicViewPage : ViewPage { public new dynamic ViewData { get { return DynamicViewDataDictionary.Wrap(base.ViewData); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ElementalValueProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web.Mvc; namespace Microsoft.Web.Mvc { // Represents a value provider that contains a single value. internal sealed class ElementalValueProvider : IValueProvider { public ElementalValueProvider(string name, object rawValue, CultureInfo culture) { Name = name; RawValue = rawValue; Culture = culture; } public CultureInfo Culture { get; private set; } public string Name { get; private set; } public object RawValue { get; private set; } public bool ContainsPrefix(string prefix) { return ValueProviderUtil.IsPrefixMatch(prefix, Name); } public ValueProviderResult GetValue(string key) { return (String.Equals(key, Name, StringComparison.OrdinalIgnoreCase)) ? new ValueProviderResult(RawValue, Convert.ToString(RawValue, Culture), Culture) : null; } } } ================================================ FILE: src/Microsoft.Web.Mvc/EmailAddressAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class EmailAddressAttribute : DataTypeAttribute, IClientValidatable { private static Regex _regex = new Regex(@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); public EmailAddressAttribute() : base(DataType.EmailAddress) { ErrorMessage = MvcResources.EmailAddressAttribute_Invalid; } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ValidationType = "email", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; } public override bool IsValid(object value) { if (value == null) { return true; } string valueAsString = value as string; return valueAsString != null && _regex.Match(valueAsString).Length > 0; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Error.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { internal static class Error { public static InvalidOperationException BindingBehavior_ValueNotFound(string fieldName) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.BindingBehavior_ValueNotFound, fieldName); return new InvalidOperationException(errorString); } public static ArgumentException Common_TypeMustImplementInterface(Type providedType, Type requiredInterfaceType, string parameterName) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.Common_TypeMustImplementInterface, providedType, requiredInterfaceType); return new ArgumentException(errorString, parameterName); } public static ArgumentException GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType(Type specifiedType, string parameterName) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType, specifiedType); return new ArgumentException(errorString, parameterName); } public static ArgumentException GenericModelBinderProvider_TypeArgumentCountMismatch(Type modelType, Type modelBinderType) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.GenericModelBinderProvider_TypeArgumentCountMismatch, modelType, modelType.GetGenericArguments().Length, modelBinderType, modelBinderType.GetGenericArguments().Length); return new ArgumentException(errorString, "modelBinderType"); } public static InvalidOperationException ModelBinderProviderCollection_BinderForTypeNotFound(Type modelType) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderProviderCollection_BinderForTypeNotFound, modelType); return new InvalidOperationException(errorString); } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this class is to throw errors on behalf of other methods")] public static ArgumentException ModelBinderUtil_ModelCannotBeNull(Type expectedType) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderUtil_ModelCannotBeNull, expectedType); return new ArgumentException(errorString, "bindingContext"); } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this class is to throw errors on behalf of other methods")] public static ArgumentException ModelBinderUtil_ModelInstanceIsWrong(Type actualType, Type expectedType) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderUtil_ModelInstanceIsWrong, actualType, expectedType); return new ArgumentException(errorString, "bindingContext"); } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this class is to throw errors on behalf of other methods")] public static ArgumentException ModelBinderUtil_ModelMetadataCannotBeNull() { return new ArgumentException(MvcResources.ModelBinderUtil_ModelMetadataCannotBeNull, "bindingContext"); } [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The purpose of this class is to throw errors on behalf of other methods")] public static ArgumentException ModelBinderUtil_ModelTypeIsWrong(Type actualType, Type expectedType) { string errorString = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderUtil_ModelTypeIsWrong, actualType, expectedType); return new ArgumentException(errorString, "bindingContext"); } public static InvalidOperationException ModelBindingContext_ModelMetadataMustBeSet() { return new InvalidOperationException(MvcResources.ModelBindingContext_ModelMetadataMustBeSet); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/BinaryExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // BinaryExpression fingerprint class // Useful for things like array[index] [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class BinaryExpressionFingerprint : ExpressionFingerprint { public BinaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on BinaryExpression (like IsLifted / IsLiftedToNull) are simply derived // from Type and NodeType, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.binaryexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { BinaryExpressionFingerprint other = obj as BinaryExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/CachedExpressionCompiler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; namespace Microsoft.Web.Mvc.ExpressionUtil { internal static class CachedExpressionCompiler { // This is the entry point to the cached expression compilation system. The system // will try to turn the expression into an actual delegate as quickly as possible, // relying on cache lookups and other techniques to save time if appropriate. // If the provided expression is particularly obscure and the system doesn't know // how to handle it, we'll just compile the expression as normal. public static Func Process(Expression> lambdaExpression) { return Compiler.Compile(lambdaExpression); } private static class Compiler { private static Func _identityFunc; private static readonly ConcurrentDictionary> _simpleMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _constMemberAccessDict = new ConcurrentDictionary>(); private static readonly ConcurrentDictionary> _fingerprintedCache = new ConcurrentDictionary>(); public static Func Compile(Expression> expr) { return CompileFromIdentityFunc(expr) ?? CompileFromConstLookup(expr) ?? CompileFromMemberAccess(expr) ?? CompileFromFingerprint(expr) ?? CompileSlow(expr); } private static Func CompileFromConstLookup(Expression> expr) { ConstantExpression constExpr = expr.Body as ConstantExpression; if (constExpr != null) { // model => {const} TOut constantValue = (TOut)constExpr.Value; return _ => constantValue; } return null; } private static Func CompileFromIdentityFunc(Expression> expr) { if (expr.Body == expr.Parameters[0]) { // model => model // don't need to lock, as all identity funcs are identical if (_identityFunc == null) { _identityFunc = expr.Compile(); } return _identityFunc; } return null; } private static Func CompileFromFingerprint(Expression> expr) { List capturedConstants; ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants); if (fingerprint != null) { var del = _fingerprintedCache.GetOrAdd(fingerprint, _ => { // Fingerprinting succeeded, but there was a cache miss. Rewrite the expression // and add the rewritten expression to the cache. var hoistedExpr = HoistingExpressionVisitor.Hoist(expr); return hoistedExpr.Compile(); }); return model => del(model, capturedConstants); } // couldn't be fingerprinted return null; } private static Func CompileFromMemberAccess(Expression> expr) { // Performance tests show that on the x64 platform, special-casing static member and // captured local variable accesses is faster than letting the fingerprinting system // handle them. On the x86 platform, the fingerprinting system is faster, but only // by around one microsecond, so it's not worth it to complicate the logic here with // an architecture check. MemberExpression memberExpr = expr.Body as MemberExpression; if (memberExpr != null) { if (memberExpr.Expression == expr.Parameters[0] || memberExpr.Expression == null) { // model => model.Member or model => StaticMember return _simpleMemberAccessDict.GetOrAdd(memberExpr.Member, _ => expr.Compile()); } ConstantExpression constExpr = memberExpr.Expression as ConstantExpression; if (constExpr != null) { // model => {const}.Member (captured local variable) var del = _constMemberAccessDict.GetOrAdd(memberExpr.Member, _ => { // rewrite as capturedLocal => ((TDeclaringType)capturedLocal).Member var constParamExpr = Expression.Parameter(typeof(object), "capturedLocal"); var constCastExpr = Expression.Convert(constParamExpr, memberExpr.Member.DeclaringType); var newMemberAccessExpr = memberExpr.Update(constCastExpr); var newLambdaExpr = Expression.Lambda>(newMemberAccessExpr, constParamExpr); return newLambdaExpr.Compile(); }); object capturedLocal = constExpr.Value; return _ => del(capturedLocal); } } return null; } private static Func CompileSlow(Expression> expr) { // fallback compilation system - just compile the expression directly return expr.Compile(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/ConditionalExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // ConditionalExpression fingerprint class // Expression of form (test) ? ifTrue : ifFalse [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class ConditionalExpressionFingerprint : ExpressionFingerprint { public ConditionalExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on ConditionalExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { ConditionalExpressionFingerprint other = obj as ConditionalExpressionFingerprint; return (other != null) && this.Equals(other); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/ConstantExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // ConstantExpression fingerprint class // // A ConstantExpression might represent a captured local variable, so we can't compile // the value directly into the cached function. Instead, a placeholder is generated // and the value is hoisted into a local variables array. This placeholder can then // be compiled and cached, and the array lookup happens at runtime. [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class ConstantExpressionFingerprint : ExpressionFingerprint { public ConstantExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on ConstantExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { ConstantExpressionFingerprint other = obj as ConstantExpressionFingerprint; return (other != null) && this.Equals(other); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/DefaultExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // DefaultExpression fingerprint class // Expression of form default(T) [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class DefaultExpressionFingerprint : ExpressionFingerprint { public DefaultExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on DefaultExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { DefaultExpressionFingerprint other = obj as DefaultExpressionFingerprint; return (other != null) && this.Equals(other); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/ExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq.Expressions; namespace Microsoft.Web.Mvc.ExpressionUtil { // Serves as the base class for all expression fingerprints. Provides a default implementation // of GetHashCode(). internal abstract class ExpressionFingerprint { protected ExpressionFingerprint(ExpressionType nodeType, Type type) { NodeType = nodeType; Type = type; } // the type of expression node, e.g. OP_ADD, MEMBER_ACCESS, etc. public ExpressionType NodeType { get; private set; } // the CLR type resulting from this expression, e.g. int, string, etc. public Type Type { get; private set; } internal virtual void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddInt32((int)NodeType); combiner.AddObject(Type); } protected bool Equals(ExpressionFingerprint other) { return (other != null) && (this.NodeType == other.NodeType) && Equals(this.Type, other.Type); } public override bool Equals(object obj) { return Equals(obj as ExpressionFingerprint); } public override int GetHashCode() { HashCodeCombiner combiner = new HashCodeCombiner(); AddToHashCodeCombiner(combiner); return combiner.CombinedHash; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/ExpressionFingerprintChain.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace Microsoft.Web.Mvc.ExpressionUtil { // Expression fingerprint chain class // Contains information used for generalizing, comparing, and recreating Expression instances // // Since Expression objects are immutable and are recreated for every invocation of an expression // helper method, they can't be compared directly. Fingerprinting Expression objects allows // information about them to be abstracted away, and the fingerprints can be directly compared. // Consider the process of fingerprinting that all values (parameters, constants, etc.) are hoisted // and replaced with dummies. What remains can be decomposed into a sequence of operations on specific // types and specific inputs. // // Some sample fingerprints chains: // // 2 + 4 -> OP_ADD, CONST:int, NULL, CONST:int // 2 + 8 -> OP_ADD, CONST:int, NULL, CONST:int // 2.0 + 4.0 -> OP_ADD, CONST:double, NULL, CONST:double // // 2 + 4 and 2 + 8 have the same fingerprint, but 2.0 + 4.0 has a different fingerprint since its // underlying types differ. Note that this looks a bit like prefix notation and is a side effect // of how the ExpressionVisitor class recurses into expressions. (Occasionally there will be a NULL // in the fingerprint chain, which depending on context can denote a static member, a null Conversion // in a BinaryExpression, and so forth.) // // "Hello " + "world" -> OP_ADD, CONST:string, NULL, CONST:string // "Hello " + {model} -> OP_ADD, CONST:string, NULL, PARAM_0:string // // These string concatenations have different fingerprints since the inputs are provided differently: // one is a constant, the other is a parameter. // // ({model} ?? "sample").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string // ({model} ?? "other sample").Length -> MEMBER_ACCESS(String.Length), OP_COALESCE, PARAM_0:string, NULL, CONST:string // // These expressions have the same fingerprint since all constants of the same underlying type are // treated equally. // // It's also important that the fingerprints don't reference the actual Expression objects that were // used to generate them, as the fingerprints will be cached, and caching a fingerprint that references // an Expression will root the Expression (and any objects it references). internal sealed class ExpressionFingerprintChain : IEquatable { public readonly List Elements = new List(); public bool Equals(ExpressionFingerprintChain other) { // Two chains are considered equal if two elements appearing in the same index in // each chain are equal (value equality, not referential equality). if (other == null) { return false; } if (this.Elements.Count != other.Elements.Count) { return false; } for (int i = 0; i < this.Elements.Count; i++) { if (!Equals(this.Elements[i], other.Elements[i])) { return false; } } return true; } public override bool Equals(object obj) { return Equals(obj as ExpressionFingerprintChain); } public override int GetHashCode() { HashCodeCombiner combiner = new HashCodeCombiner(); Elements.ForEach(combiner.AddFingerprint); return combiner.CombinedHash; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/FingerprintingExpressionVisitor.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq.Expressions; namespace Microsoft.Web.Mvc.ExpressionUtil { // This is a visitor which produces a fingerprint of an expression. It doesn't // rewrite the expression in a form which can be compiled and cached. internal sealed class FingerprintingExpressionVisitor : ExpressionVisitor { private readonly List _seenConstants = new List(); private readonly List _seenParameters = new List(); private readonly ExpressionFingerprintChain _currentChain = new ExpressionFingerprintChain(); private bool _gaveUp; private FingerprintingExpressionVisitor() { } private T GiveUp(T node) { // We don't understand this node, so just quit. _gaveUp = true; return node; } // Returns the fingerprint chain + captured constants list for this expression, or null // if the expression couldn't be fingerprinted. public static ExpressionFingerprintChain GetFingerprintChain(Expression expr, out List capturedConstants) { FingerprintingExpressionVisitor visitor = new FingerprintingExpressionVisitor(); visitor.Visit(expr); if (visitor._gaveUp) { capturedConstants = null; return null; } else { capturedConstants = visitor._seenConstants; return visitor._currentChain; } } public override Expression Visit(Expression node) { if (node == null) { _currentChain.Elements.Add(null); return null; } else { return base.Visit(node); } } protected override Expression VisitBinary(BinaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new BinaryExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitBinary(node); } protected override Expression VisitBlock(BlockExpression node) { return GiveUp(node); } protected override CatchBlock VisitCatchBlock(CatchBlock node) { return GiveUp(node); } protected override Expression VisitConditional(ConditionalExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new ConditionalExpressionFingerprint(node.NodeType, node.Type)); return base.VisitConditional(node); } protected override Expression VisitConstant(ConstantExpression node) { if (_gaveUp) { return node; } _seenConstants.Add(node.Value); _currentChain.Elements.Add(new ConstantExpressionFingerprint(node.NodeType, node.Type)); return base.VisitConstant(node); } protected override Expression VisitDebugInfo(DebugInfoExpression node) { return GiveUp(node); } protected override Expression VisitDefault(DefaultExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new DefaultExpressionFingerprint(node.NodeType, node.Type)); return base.VisitDefault(node); } protected override Expression VisitDynamic(DynamicExpression node) { return GiveUp(node); } protected override ElementInit VisitElementInit(ElementInit node) { return GiveUp(node); } protected override Expression VisitExtension(Expression node) { return GiveUp(node); } protected override Expression VisitGoto(GotoExpression node) { return GiveUp(node); } protected override Expression VisitIndex(IndexExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new IndexExpressionFingerprint(node.NodeType, node.Type, node.Indexer)); return base.VisitIndex(node); } protected override Expression VisitInvocation(InvocationExpression node) { return GiveUp(node); } protected override Expression VisitLabel(LabelExpression node) { return GiveUp(node); } protected override LabelTarget VisitLabelTarget(LabelTarget node) { return GiveUp(node); } protected override Expression VisitLambda(Expression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new LambdaExpressionFingerprint(node.NodeType, node.Type)); return base.VisitLambda(node); } protected override Expression VisitListInit(ListInitExpression node) { return GiveUp(node); } protected override Expression VisitLoop(LoopExpression node) { return GiveUp(node); } protected override Expression VisitMember(MemberExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new MemberExpressionFingerprint(node.NodeType, node.Type, node.Member)); return base.VisitMember(node); } protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) { return GiveUp(node); } protected override MemberBinding VisitMemberBinding(MemberBinding node) { return GiveUp(node); } protected override Expression VisitMemberInit(MemberInitExpression node) { return GiveUp(node); } protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) { return GiveUp(node); } protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) { return GiveUp(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new MethodCallExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitMethodCall(node); } protected override Expression VisitNew(NewExpression node) { return GiveUp(node); } protected override Expression VisitNewArray(NewArrayExpression node) { return GiveUp(node); } protected override Expression VisitParameter(ParameterExpression node) { if (_gaveUp) { return node; } int parameterIndex = _seenParameters.IndexOf(node); if (parameterIndex < 0) { // first time seeing this parameter parameterIndex = _seenParameters.Count; _seenParameters.Add(node); } _currentChain.Elements.Add(new ParameterExpressionFingerprint(node.NodeType, node.Type, parameterIndex)); return base.VisitParameter(node); } protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { return GiveUp(node); } protected override Expression VisitSwitch(SwitchExpression node) { return GiveUp(node); } protected override SwitchCase VisitSwitchCase(SwitchCase node) { return GiveUp(node); } protected override Expression VisitTry(TryExpression node) { return GiveUp(node); } protected override Expression VisitTypeBinary(TypeBinaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new TypeBinaryExpressionFingerprint(node.NodeType, node.Type, node.TypeOperand)); return base.VisitTypeBinary(node); } protected override Expression VisitUnary(UnaryExpression node) { if (_gaveUp) { return node; } _currentChain.Elements.Add(new UnaryExpressionFingerprint(node.NodeType, node.Type, node.Method)); return base.VisitUnary(node); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/HashCodeCombiner.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; namespace Microsoft.Web.Mvc.ExpressionUtil { // based on System.Web.Util.HashCodeCombiner internal class HashCodeCombiner { private long _combinedHash64 = 0x1505L; public int CombinedHash { get { return _combinedHash64.GetHashCode(); } } public void AddFingerprint(ExpressionFingerprint fingerprint) { if (fingerprint != null) { fingerprint.AddToHashCodeCombiner(this); } else { AddInt32(0); } } public void AddEnumerable(IEnumerable e) { if (e == null) { AddInt32(0); } else { int count = 0; foreach (object o in e) { AddObject(o); count++; } AddInt32(count); } } public void AddInt32(int i) { _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; } public void AddObject(object o) { int hashCode = (o != null) ? o.GetHashCode() : 0; AddInt32(hashCode); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/Hoisted.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.Web.Mvc.ExpressionUtil { internal delegate TValue Hoisted(TModel model, List capturedConstants); } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/HoistingExpressionVisitor.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Microsoft.Web.Mvc.ExpressionUtil { // This is a visitor which rewrites constant expressions as parameter lookups. It's meant // to produce an expression which can be cached safely. internal sealed class HoistingExpressionVisitor : ExpressionVisitor { private static readonly ParameterExpression _hoistedConstantsParamExpr = Expression.Parameter(typeof(List), "hoistedConstants"); private int _numConstantsProcessed; // factory will create instance private HoistingExpressionVisitor() { } public static Expression> Hoist(Expression> expr) { // rewrite Expression> as Expression> var visitor = new HoistingExpressionVisitor(); var rewrittenBodyExpr = visitor.Visit(expr.Body); var rewrittenLambdaExpr = Expression.Lambda>(rewrittenBodyExpr, expr.Parameters[0], _hoistedConstantsParamExpr); return rewrittenLambdaExpr; } protected override Expression VisitConstant(ConstantExpression node) { // rewrite the constant expression as (TConst)hoistedConstants[i]; return Expression.Convert(Expression.Property(_hoistedConstantsParamExpr, "Item", Expression.Constant(_numConstantsProcessed++)), node.Type); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/IndexExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // IndexExpression fingerprint class // Represents certain forms of array access or indexer property access [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class IndexExpressionFingerprint : ExpressionFingerprint { public IndexExpressionFingerprint(ExpressionType nodeType, Type type, PropertyInfo indexer) : base(nodeType, type) { // Other properties on IndexExpression (like the argument count) are simply derived // from Type and Indexer, so they're not necessary for inclusion in the fingerprint. Indexer = indexer; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.indexexpression.indexer.aspx public PropertyInfo Indexer { get; private set; } public override bool Equals(object obj) { IndexExpressionFingerprint other = obj as IndexExpressionFingerprint; return (other != null) && Equals(this.Indexer, other.Indexer) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Indexer); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/LambdaExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // LambdaExpression fingerprint class // Represents a lambda expression (root element in Expression) [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class LambdaExpressionFingerprint : ExpressionFingerprint { public LambdaExpressionFingerprint(ExpressionType nodeType, Type type) : base(nodeType, type) { // There are no properties on LambdaExpression that are worth including in // the fingerprint. } public override bool Equals(object obj) { LambdaExpressionFingerprint other = obj as LambdaExpressionFingerprint; return (other != null) && this.Equals(other); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/MemberExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // MemberExpression fingerprint class // Expression of form xxx.FieldOrProperty [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class MemberExpressionFingerprint : ExpressionFingerprint { public MemberExpressionFingerprint(ExpressionType nodeType, Type type, MemberInfo member) : base(nodeType, type) { Member = member; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.memberexpression.member.aspx public MemberInfo Member { get; private set; } public override bool Equals(object obj) { MemberExpressionFingerprint other = obj as MemberExpressionFingerprint; return (other != null) && Equals(this.Member, other.Member) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Member); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/MethodCallExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // MethodCallExpression fingerprint class // Expression of form xxx.Foo(...), xxx[...] (get_Item()), etc. [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class MethodCallExpressionFingerprint : ExpressionFingerprint { public MethodCallExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on MethodCallExpression (like the argument count) are simply derived // from Type and Indexer, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.methodcallexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { MethodCallExpressionFingerprint other = obj as MethodCallExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/ParameterExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // ParameterExpression fingerprint class // Can represent the model parameter or an inner parameter in an open lambda expression [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class ParameterExpressionFingerprint : ExpressionFingerprint { public ParameterExpressionFingerprint(ExpressionType nodeType, Type type, int parameterIndex) : base(nodeType, type) { ParameterIndex = parameterIndex; } // Parameter position within the overall expression, used to maintain alpha equivalence. public int ParameterIndex { get; private set; } public override bool Equals(object obj) { ParameterExpressionFingerprint other = obj as ParameterExpressionFingerprint; return (other != null) && (this.ParameterIndex == other.ParameterIndex) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddInt32(ParameterIndex); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/TypeBinaryExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // TypeBinary fingerprint class // Expression of form "obj is T" [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class TypeBinaryExpressionFingerprint : ExpressionFingerprint { public TypeBinaryExpressionFingerprint(ExpressionType nodeType, Type type, Type typeOperand) : base(nodeType, type) { TypeOperand = typeOperand; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.typebinaryexpression.typeoperand.aspx public Type TypeOperand { get; private set; } public override bool Equals(object obj) { TypeBinaryExpressionFingerprint other = obj as TypeBinaryExpressionFingerprint; return (other != null) && Equals(this.TypeOperand, other.TypeOperand) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(TypeOperand); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ExpressionUtil/UnaryExpressionFingerprint.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; #pragma warning disable 659 // overrides AddToHashCodeCombiner instead namespace Microsoft.Web.Mvc.ExpressionUtil { // UnaryExpression fingerprint class // The most common appearance of a UnaryExpression is a cast or other conversion operator [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals", Justification = "Overrides AddToHashCodeCombiner() instead.")] internal sealed class UnaryExpressionFingerprint : ExpressionFingerprint { public UnaryExpressionFingerprint(ExpressionType nodeType, Type type, MethodInfo method) : base(nodeType, type) { // Other properties on UnaryExpression (like IsLifted / IsLiftedToNull) are simply derived // from Type and NodeType, so they're not necessary for inclusion in the fingerprint. Method = method; } // http://msdn.microsoft.com/en-us/library/system.linq.expressions.unaryexpression.method.aspx public MethodInfo Method { get; private set; } public override bool Equals(object obj) { UnaryExpressionFingerprint other = obj as UnaryExpressionFingerprint; return (other != null) && Equals(this.Method, other.Method) && this.Equals(other); } internal override void AddToHashCodeCombiner(HashCodeCombiner combiner) { combiner.AddObject(Method); base.AddToHashCodeCombiner(combiner); } } } ================================================ FILE: src/Microsoft.Web.Mvc/FileExtensionsAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Web; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class FileExtensionsAttribute : DataTypeAttribute, IClientValidatable { private string _extensions; public FileExtensionsAttribute() : base("upload") { ErrorMessage = MvcResources.FileExtensionsAttribute_Invalid; } public string Extensions { get { return String.IsNullOrWhiteSpace(_extensions) ? "png,jpg,jpeg,gif" : _extensions; } set { _extensions = value; } } private string ExtensionsFormatted { get { return ExtensionsParsed.Aggregate((left, right) => left + ", " + right); } } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")] private string ExtensionsNormalized { get { return Extensions.Replace(" ", String.Empty).Replace(".", String.Empty).ToLowerInvariant(); } } private IEnumerable ExtensionsParsed { get { return ExtensionsNormalized.Split(',').Select(e => "." + e); } } public override string FormatErrorMessage(string name) { return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, ExtensionsFormatted); } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule { ValidationType = "extension", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; rule.ValidationParameters["extension"] = ExtensionsNormalized; yield return rule; } public override bool IsValid(object value) { if (value == null) { return true; } HttpPostedFileBase valueAsFileBase = value as HttpPostedFileBase; if (valueAsFileBase != null) { return ValidateExtension(valueAsFileBase.FileName); } string valueAsString = value as string; if (valueAsString != null) { return ValidateExtension(valueAsString); } return false; } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are normalized to lowercase because they are presented to the user in lowercase format")] private bool ValidateExtension(string fileName) { try { return ExtensionsParsed.Contains(Path.GetExtension(fileName).ToLowerInvariant()); } catch (ArgumentException) { return false; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/FormExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Mvc.Html; namespace Microsoft.Web.Mvc { public static class FormExtensions { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcForm BeginForm(this HtmlHelper helper, Expression> action) where TController : Controller { return BeginForm(helper, action, FormMethod.Post, null); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcForm BeginForm(this HtmlHelper helper, Expression> action, FormMethod method) where TController : Controller { return BeginForm(helper, action, method, null); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcForm BeginForm(this HtmlHelper helper, Expression> action, FormMethod method, object htmlAttributes) where TController : Controller { return BeginForm(helper, action, method, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcForm BeginForm(this HtmlHelper helper, Expression> action, FormMethod method, IDictionary htmlAttributes) where TController : Controller { TagBuilder tagBuilder = new TagBuilder("form"); tagBuilder.MergeAttributes(htmlAttributes); string formAction = helper.BuildUrlFromExpression(action); tagBuilder.MergeAttribute("action", formAction); tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method)); helper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag)); return new MvcForm(helper.ViewContext); } } } ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Boolean.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (ViewData.ModelMetadata.IsNullableValueType) { %> <% } else { %> /> <% } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Collection.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (Model != null) { string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix; int index = 0; ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty; foreach (object item in (IEnumerable)Model) { string fieldName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", oldPrefix, index++); ViewContext.Writer.Write(Html.DisplayFor(m => item, null, fieldName)); } ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix; } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Decimal.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.Encode(FormattedValue) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/EmailAddress.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/HiddenInput.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (!ViewData.ModelMetadata.HideSurroundingHtml) { %> <%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %> <% } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Html.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= ViewData.TemplateInfo.FormattedModelValue %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Object.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (Model == null) { %> <%= ViewData.ModelMetadata.NullDisplayText %> <% } else if (ViewData.TemplateInfo.TemplateDepth > 1) { %> <%= ViewData.ModelMetadata.SimpleDisplayText %> <% } else { %> <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm))) { %> <% if (prop.HideSurroundingHtml) { %> <%= Html.Display(prop.PropertyName) %> <% } else { %> <% if (!String.IsNullOrEmpty(prop.GetDisplayName())) { %>
<%= prop.GetDisplayName() %>
<% } %>
<%= Html.Display(prop.PropertyName) %>
<% } %> <% } %> <% } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/String.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/DisplayTemplates/Url.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/Boolean.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (ViewData.ModelMetadata.IsNullableValueType) { %> <%= Html.DropDownList("", TriStateValues, new { @class = "list-box tri-state" }) %> <% } else { %> <%= Html.CheckBox("", Value ?? false, new { @class = "check-box" }) %> <% } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/Collection.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (Model != null) { string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix; int index = 0; ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty; foreach (object item in (IEnumerable)Model) { string fieldName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", oldPrefix, index++); ViewContext.Writer.Write(Html.EditorFor(m => item, null, fieldName)); } ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix; } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/Decimal.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.TextBox("", FormattedValue, new { @class = "text-box single-line" }) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/HiddenInput.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (!ViewData.ModelMetadata.HideSurroundingHtml) { %> <%= Html.Encode(ViewData.TemplateInfo.FormattedModelValue) %> <% } %> <%= Html.Hidden("", ModelValue) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/MultilineText.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.TextArea("", ViewData.TemplateInfo.FormattedModelValue.ToString(), 0, 0, new { @class = "text-box multi-line" }) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/Object.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% if (ViewData.TemplateInfo.TemplateDepth > 1) { %> <% if (Model == null) { %> <%= ViewData.ModelMetadata.NullDisplayText %> <% } else { %> <%= ViewData.ModelMetadata.SimpleDisplayText %> <% } %> <% } else { %> <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => ShouldShow(pm))) { %> <% if (prop.HideSurroundingHtml) { %> <%= Html.Editor(prop.PropertyName) %> <% } else { %> <% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
<%= Html.Label(prop.PropertyName) %>
<% } %>
<%= Html.Editor(prop.PropertyName) %> <%= Html.ValidationMessage(prop.PropertyName, "*") %>
<% } %> <% } %> <% } %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/Password.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.Password("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line password" })%> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/DefaultTemplates/EditorTemplates/String.ascx ================================================ <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line" }) %> ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/iismap.vbs ================================================ Const HKEY_LOCAL_MACHINE = &H80000002 Const MACHINE_NAME = "localhost" Const DEFAULT_PATH = "W3SVC" Const SCRIPT_MAPS = "ScriptMaps" Function ExtensionExists(extension, fxVersion) Dim scriptExtension iisScriptMaps = GetScriptMaps() For scriptIndex = 0 To UBound(iisScriptMaps) scriptMap = iisScriptMaps(scriptIndex) decomposedScriptMap = Split(scriptMap, ",") scriptExtension = Right(decomposedScriptMap(0), Len(decomposedScriptMap(0))-1) If StrComp(LCase(scriptExtension), LCase(extension)) = 0 Then If InStr(scriptMap, fxVersion) > 0 Then ExtensionExists = true End If End If Next ExtensionExists = false End Function Function GetFrameworkPath(fxVersion) strComputer = "." Set registryObj = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") strKeyPath = "SOFTWARE\Microsoft\.NETFramework" strValueName = "InstallRoot" registryObj.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue GetFrameworkPath = strValue & fxVersion End Function Function GetFrameworkVersions() strComputer = "." Set registryObj = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") strKeyPath = "SOFTWARE\Microsoft\.NETFramework" registryObj.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubKeys Set regex = New RegExp regex.Pattern = "^(v(2|4)\.\d+\.\d+(\.\d+)?)$" regex.Global = True ReDim fxVersions(0) For Each key in arrSubKeys If regex.Test(key) Then ReDim Preserve fxVersions(UBound(fxVersions)+1) fxVersions(UBound(fxVersions)-1) = key End If Next If UBound(fxVersions) > 0 Then Redim Preserve fxVersions(UBound(fxVersions)-1) End If GetFrameworkVersions = fxVersions End Function Function IsValidFrameworkVersion(fxVersion) strComputer = "." Set registryObj = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") strKeyPath = "SOFTWARE\Microsoft\.NETFramework\" & fxVersion registryObj.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubKeys IsValidFrameworkVersion = Not IsNull(arrSubKeys) End Function Function GetScriptMaps() Dim iisObject Set iisObject = GetObject("IIS://" & MACHINE_NAME & "/" & DEFAULT_PATH) GetScriptMaps = iisObject.Get(SCRIPT_MAPS) End Function Sub RegisterExtension(extension, fxVersion) Dim iisScriptMaps Set iisObject = GetObject("IIS://" & MACHINE_NAME & "/" & DEFAULT_PATH) iisScriptMaps = GetScriptMaps() If ExtensionExists(extension, fxVersion) Then WScript.Echo extension & " is already registered for .NET Framework " & fxVersion & "." WScript.Quit End If ReDim Preserve iisScriptMaps(UBound(iisScriptMaps)+1) iisScriptMaps(UBound(iisScriptMaps)) = "." & extension & "," & GetFrameworkPath(fxVersion) & "\aspnet_isapi.dll,1,GET,HEAD,POST" iisObject.Put "ScriptMaps", iisScriptMaps iisObject.Setinfo End Sub Sub SetScriptMaps(ScriptMaps) Dim iisObject Set iisObject = GetObject("ISS://" & MACHINE_NAME & "/" & DEFAULT_PATH) iisObject.Put SCRIPT_MAPS, ScriptMaps iisObject.Setinfo End Sub Sub UnregisterExtension(extension, fxVersion) Dim newScriptMaps Set iisObject = GetObject("IIS://" & MACHINE_NAME & "/" & DEFAULT_PATH) iisScriptMaps = GetScriptMaps() If Not ExtensionExists(extension, fxVersion) Then WScript.Echo extension & " is not registered for .NET Framework " & fxVersion & "." WScript.Quit End If ReDim newScriptMaps(UBound(iisScriptMaps)-1) Dim newScriptIndex newScriptIndex = 0 For scriptIndex = 0 To UBound(iisScriptMaps) scriptMap = iisScriptMaps(scriptIndex) decomposedScriptMap = Split(scriptMap, ",") scriptExtension = Right(decomposedScriptMap(0), Len(decomposedScriptMap(0))-1) If (StrComp(LCase(scriptExtension), LCase(extension)) <> 0) Or ((StrComp(LCase(scriptExtension), LCase(extension)) = 0) And (InStr(scriptMap, fxVersion) = 0)) Then newScriptMaps(newScriptIndex) = scriptMap newScriptIndex = newScriptIndex + 1 End If Next iisObject.Put "ScriptMaps", newScriptMaps iisObject.Setinfo End Sub ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/registermvc.wsf ================================================ ================================================ FILE: src/Microsoft.Web.Mvc/FuturesFiles/unregistermvc.wsf ================================================ ================================================ FILE: src/Microsoft.Web.Mvc/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Web.Mvc.Html", Justification = "Helpers reside within a separate namespace to support alternate helper classes.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "Microsoft.Web.Mvc.Properties.MvcResources.resources", Justification = "The spelling is correct.")] [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Assembly is delay-signed.")] ================================================ FILE: src/Microsoft.Web.Mvc/Html/HtmlHelperExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; namespace Microsoft.Web.Mvc.Html { // This class contains definitions of single "super" HTML helper methods, which rely on // CLR 4's default values for method parameters to make them more consumable. Methods // which previously took an HTML attributes object/dictionary now have their legal // attribute values all available as optional parameters. Some attributes are only // applicable for some DTDs; deprecated attributes (like "align" on input) were // specifically excluded. // // Since htmlAttributes was very often the last parameter to HTML helper methods, // converting to these new syntaxes should be as simple as converting your anonymous // object htmlAttributes collections into optional parameters. // // Where there were two overloads for route values (anonymous object and dictionary), // there is only a single overload now, which takes type object. If what you pass is // a dictionary of the correct type, then we'll use that; otherwise, we'll assume it's // an anonymous object and create the dictionary for you. This should make it simple // to port methods using route values, as they should just continue to work as before. // // Some HTML helpers did not take HTML attributes parameters. They are recreated here // so that the user does not have to import both System.Web.Mvc.Html as well as this // namespace, since the purpose of these methods is to get rid of all the overloads // for the built-in HTML helpers. // // The legal attribute values were derived from: http://www.w3schools.com/tags/ public static class HtmlHelperExtensions { // ChildActionExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName = null, object routeValues = null) { return ChildActionExtensions.Action( htmlHelper, actionName, controllerName, routeValues as IDictionary ?? new RouteValueDictionary(routeValues)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static void RenderAction(this HtmlHelper htmlHelper, string actionName, string controllerName = null, object routeValues = null) { ChildActionExtensions.RenderAction( htmlHelper, actionName, controllerName, routeValues as IDictionary ?? new RouteValueDictionary(routeValues)); } // DisplayExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Display(this HtmlHelper htmlHelper, string expression, string templateName = null, string htmlFieldName = null) { return DisplayExtensions.Display(htmlHelper, expression, templateName, htmlFieldName); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString DisplayFor(this HtmlHelper htmlHelper, Expression> expression, string templateName = null, string htmlFieldName = null) { return DisplayExtensions.DisplayFor(htmlHelper, expression, templateName, htmlFieldName); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString DisplayForModel(this HtmlHelper htmlHelper, string templateName = null, string htmlFieldName = null) { return DisplayExtensions.DisplayForModel(htmlHelper, templateName, htmlFieldName); } // DisplayTextExtensions public static MvcHtmlString DisplayText(this HtmlHelper htmlHelper, string name) { return DisplayTextExtensions.DisplayText(htmlHelper, name); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcHtmlString DisplayTextFor(this HtmlHelper htmlHelper, Expression> expression) { return DisplayTextExtensions.DisplayTextFor(htmlHelper, expression); } // EditorExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Editor(this HtmlHelper htmlHelper, string expression, string templateName = null, string htmlFieldName = null) { return EditorExtensions.Editor(htmlHelper, expression, templateName, htmlFieldName); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString EditorFor(this HtmlHelper htmlHelper, Expression> expression, string templateName = null, string htmlFieldName = null) { return EditorExtensions.EditorFor(htmlHelper, expression, templateName, htmlFieldName); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString EditorForModel(this HtmlHelper htmlHelper, string templateName = null, string htmlFieldName = null) { return EditorExtensions.EditorForModel(htmlHelper, templateName, htmlFieldName); } // FormExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName = null, string controllerName = null, object routeValues = null, FormMethod method = FormMethod.Post, string accept = null, string acceptCharset = null, string cssClass = null, string dir = null, string encType = null, string id = null, string lang = null, string name = null, string style = null, string title = null) { return htmlHelper.BeginForm( actionName, controllerName, routeValues as RouteValueDictionary ?? new RouteValueDictionary(routeValues), method, FormAttributes(accept, acceptCharset, cssClass, dir, encType, id, lang, name, style, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcForm BeginRouteForm(this HtmlHelper htmlHelper, string routeName, RouteValueDictionary routeValues = null, FormMethod method = FormMethod.Post, string accept = null, string acceptCharset = null, string cssClass = null, string dir = null, string encType = null, string id = null, string lang = null, string name = null, string style = null, string title = null) { return htmlHelper.BeginRouteForm( routeName, routeValues ?? new RouteValueDictionary(routeValues), method, FormAttributes(accept, acceptCharset, cssClass, dir, encType, id, lang, name, style, title)); } public static void EndForm(this HtmlHelper htmlHelper) { System.Web.Mvc.Html.FormExtensions.EndForm(htmlHelper); } // InputExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString CheckBox(this HtmlHelper htmlHelper, string name, bool? isChecked = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { var htmlAttributes = InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title); return isChecked.HasValue ? htmlHelper.CheckBox(name, isChecked.Value, htmlAttributes) : htmlHelper.CheckBox(name, htmlAttributes); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString CheckBoxFor(this HtmlHelper htmlHelper, Expression> expression, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.CheckBoxFor( expression, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Hidden(this HtmlHelper htmlHelper, string name, object value = null, string cssClass = null, string id = null, string style = null) { return htmlHelper.Hidden(name, value, Attributes(cssClass, id, style)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString HiddenFor(this HtmlHelper htmlHelper, Expression> expression, string cssClass = null, string id = null, string style = null) { return htmlHelper.HiddenFor(expression, Attributes(cssClass, id, style)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Password(this HtmlHelper htmlHelper, string name, object value = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.Password( name, value, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString PasswordFor(this HtmlHelper htmlHelper, Expression> expression, string cssClass = null, bool disabled = false, string dir = null, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.PasswordFor( expression, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString RadioButton(this HtmlHelper htmlHelper, string name, object value, bool? isChecked = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { var htmlAttributes = InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title); return isChecked.HasValue ? htmlHelper.RadioButton(name, value, isChecked.Value, htmlAttributes) : htmlHelper.RadioButton(name, value, htmlAttributes); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString RadioButtonFor(this HtmlHelper htmlHelper, Expression> expression, object value, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.RadioButtonFor( expression, value, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.TextBox( name, value, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString TextBoxFor(this HtmlHelper htmlHelper, Expression> expression, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? maxLength = null, bool readOnly = false, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.TextBoxFor( expression, InputAttributes(cssClass, dir, disabled, id, lang, maxLength, readOnly, size, style, tabIndex, title)); } // LabelExtensions public static MvcHtmlString Label(this HtmlHelper htmlHelper, string expression) { return LabelExtensions.Label(htmlHelper, expression); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcHtmlString LabelFor(this HtmlHelper htmlHelper, Expression> expression) { return LabelExtensions.LabelFor(htmlHelper, expression); } public static MvcHtmlString LabelForModel(this HtmlHelper htmlHelper) { return LabelExtensions.LabelForModel(htmlHelper); } // LinkExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName = null, string protocol = null, string hostName = null, string fragment = null, object routeValues = null, string accessKey = null, string charset = null, string coords = null, string cssClass = null, string dir = null, string hrefLang = null, string id = null, string lang = null, string name = null, string rel = null, string rev = null, string shape = null, string style = null, string target = null, string title = null) { return htmlHelper.ActionLink( linkText, actionName, controllerName, protocol, hostName, fragment, routeValues as RouteValueDictionary ?? new RouteValueDictionary(routeValues), AnchorAttributes(accessKey, charset, coords, cssClass, dir, hrefLang, id, lang, name, rel, rev, shape, style, target, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, string protocol = null, string hostName = null, string fragment = null, object routeValues = null, string accessKey = null, string charset = null, string coords = null, string cssClass = null, string dir = null, string hrefLang = null, string id = null, string lang = null, string name = null, string rel = null, string rev = null, string shape = null, string style = null, string target = null, string title = null) { return htmlHelper.RouteLink( linkText, routeName, protocol, hostName, fragment, routeValues as RouteValueDictionary ?? new RouteValueDictionary(routeValues), AnchorAttributes(accessKey, charset, coords, cssClass, dir, hrefLang, id, lang, name, rel, rev, shape, style, target, title)); } // PartialExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model = null, ViewDataDictionary viewData = null) { return PartialExtensions.Partial( htmlHelper, partialViewName, model, viewData ?? htmlHelper.ViewData); } // RenderPartialExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model = null, ViewDataDictionary viewData = null) { RenderPartialExtensions.RenderPartial( htmlHelper, partialViewName, model, viewData ?? htmlHelper.ViewData); } // SelectExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable selectList = null, string optionLabel = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.DropDownList( name, selectList, optionLabel, SelectAttributes(cssClass, dir, disabled, id, lang, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString DropDownListFor(this HtmlHelper htmlHelper, Expression> expression, IEnumerable selectList = null, string optionLabel = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.DropDownListFor( expression, selectList, optionLabel, SelectAttributes(cssClass, dir, disabled, id, lang, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString ListBox(this HtmlHelper htmlHelper, string name, IEnumerable selectList = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.ListBox( name, selectList, SelectAttributes(cssClass, dir, disabled, id, lang, size, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString ListBoxFor(this HtmlHelper htmlHelper, Expression> expression, IEnumerable selectList = null, string cssClass = null, string dir = null, bool disabled = false, string id = null, string lang = null, int? size = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.ListBoxFor( expression, selectList, SelectAttributes(cssClass, dir, disabled, id, lang, size, style, tabIndex, title)); } // TextAreaExtensions [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString TextArea(this HtmlHelper htmlHelper, string name, string value = null, string accessKey = null, string cssClass = null, int? cols = null, string dir = null, bool disabled = false, string id = null, string lang = null, bool readOnly = false, int? rows = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.TextArea( name, value, TextAreaAttributes(accessKey, cssClass, cols, dir, disabled, id, lang, readOnly, rows, style, tabIndex, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString TextAreaFor(this HtmlHelper htmlHelper, Expression> expression, string accessKey = null, string cssClass = null, int? cols = null, string dir = null, bool disabled = false, string id = null, string lang = null, bool readOnly = false, int? rows = null, string style = null, int? tabIndex = null, string title = null) { return htmlHelper.TextAreaFor( expression, TextAreaAttributes(accessKey, cssClass, cols, dir, disabled, id, lang, readOnly, rows, style, tabIndex, title)); } // ValidationExtensions public static void Validate(this HtmlHelper htmlHelper, string modelName) { ValidationExtensions.Validate(htmlHelper, modelName); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static void ValidateFor(this HtmlHelper htmlHelper, Expression> expression) { ValidationExtensions.ValidateFor(htmlHelper, expression); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] [SuppressMessage("Microsoft.Naming", "CA1719:ParameterNamesShouldNotMatchMemberNames", MessageId = "2#", Justification = "This API has already shipped.")] public static MvcHtmlString ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage = null, string cssClass = null, string dir = null, string id = null, string lang = null, string style = null, string title = null) { return htmlHelper.ValidationMessage( modelName, validationMessage, SpanAttributes(cssClass, dir, id, lang, style, title)); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString ValidationMessageFor(this HtmlHelper htmlHelper, Expression> expression, string validationMessage = null, string cssClass = null, string dir = null, string id = null, string lang = null, string style = null, string title = null) { return htmlHelper.ValidationMessageFor( expression, validationMessage, SpanAttributes(cssClass, dir, id, lang, style, title)); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "The purpose of these helpers is to use default parameters to simplify common usage.")] public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string message = null, bool excludePropertyErrors = false, string cssClass = null, string dir = null, string id = null, string lang = null, string style = null, string title = null) { return htmlHelper.ValidationSummary( excludePropertyErrors, message, SpanAttributes(cssClass, dir, id, lang, style, title)); } // Helper methods private static void AddOptional(this IDictionary dictionary, string key, bool value) { if (value) { dictionary[key] = key; } } private static void AddOptional(this IDictionary dictionary, string key, object value) { if (value != null) { dictionary[key] = value; } } private static IDictionary Attributes(string cssClass, string id, string style) { var htmlAttributes = new RouteValueDictionary(); htmlAttributes.AddOptional("class", cssClass); htmlAttributes.AddOptional("id", id); htmlAttributes.AddOptional("style", style); return htmlAttributes; } private static IDictionary AnchorAttributes(string accessKey, string charset, string coords, string cssClass, string dir, string hrefLang, string id, string lang, string name, string rel, string rev, string shape, string style, string target, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("accesskey", accessKey); htmlAttributes.AddOptional("charset", charset); htmlAttributes.AddOptional("coords", coords); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("hreflang", hrefLang); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("name", name); htmlAttributes.AddOptional("rel", rel); htmlAttributes.AddOptional("rev", rev); htmlAttributes.AddOptional("shape", shape); htmlAttributes.AddOptional("target", target); htmlAttributes.AddOptional("title", title); return htmlAttributes; } private static IDictionary FormAttributes(string accept, string acceptCharset, string cssClass, string dir, string encType, string id, string lang, string name, string style, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("accept", accept); htmlAttributes.AddOptional("accept-charset", acceptCharset); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("enctype", encType); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("name", name); htmlAttributes.AddOptional("title", title); return htmlAttributes; } private static IDictionary InputAttributes(string cssClass, string dir, bool disabled, string id, string lang, int? maxLength, bool readOnly, int? size, string style, int? tabIndex, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("disabled", disabled); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("maxlength", maxLength); htmlAttributes.AddOptional("readonly", readOnly); htmlAttributes.AddOptional("size", size); htmlAttributes.AddOptional("tabindex", tabIndex); htmlAttributes.AddOptional("title", title); return htmlAttributes; } private static IDictionary SelectAttributes(string cssClass, string dir, bool disabled, string id, string lang, int? size, string style, int? tabIndex, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("disabled", disabled); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("size", size); htmlAttributes.AddOptional("tabindex", tabIndex); htmlAttributes.AddOptional("title", title); return htmlAttributes; } private static IDictionary SpanAttributes(string cssClass, string dir, string id, string lang, string style, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("title", title); return htmlAttributes; } private static IDictionary TextAreaAttributes(string accessKey, string cssClass, int? cols, string dir, bool disabled, string id, string lang, bool readOnly, int? rows, string style, int? tabIndex, string title) { var htmlAttributes = Attributes(cssClass, id, style); htmlAttributes.AddOptional("accesskey", accessKey); htmlAttributes.AddOptional("cols", cols); htmlAttributes.AddOptional("dir", dir); htmlAttributes.AddOptional("disabled", disabled); htmlAttributes.AddOptional("lang", lang); htmlAttributes.AddOptional("readonly", readOnly); htmlAttributes.AddOptional("rows", rows); htmlAttributes.AddOptional("style", style); htmlAttributes.AddOptional("tabindex", tabIndex); htmlAttributes.AddOptional("title", title); return htmlAttributes; } } } ================================================ FILE: src/Microsoft.Web.Mvc/HtmlButtonType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc { public enum HtmlButtonType { Button, Submit, Reset, } } ================================================ FILE: src/Microsoft.Web.Mvc/IMachineKey.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Security; namespace Microsoft.Web.Mvc { // Used for mocking out the static MachineKey type internal interface IMachineKey { byte[] Unprotect(string protectedData, params string[] purposes); string Protect(byte[] userData, params string[] purposes); } } ================================================ FILE: src/Microsoft.Web.Mvc/ImageExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public static class ImageExtensions { [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl) { return Image(helper, imageRelativeUrl, null, null); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "Required for Extension Method")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, string alt) { return Image(helper, imageRelativeUrl, alt, null); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, string alt, object htmlAttributes) { return Image(helper, imageRelativeUrl, alt, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, object htmlAttributes) { return Image(helper, imageRelativeUrl, null, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, IDictionary htmlAttributes) { return Image(helper, imageRelativeUrl, null, htmlAttributes); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static MvcHtmlString Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary htmlAttributes) { if (String.IsNullOrEmpty(imageRelativeUrl)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl"); } string imageUrl = UrlHelper.GenerateContentUrl(imageRelativeUrl, helper.ViewContext.HttpContext); return MvcHtmlString.Create(Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing)); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")] public static TagBuilder Image(string imageUrl, string alt, IDictionary htmlAttributes) { if (String.IsNullOrEmpty(imageUrl)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageUrl"); } TagBuilder imageTag = new TagBuilder("img"); if (!String.IsNullOrEmpty(imageUrl)) { imageTag.MergeAttribute("src", imageUrl); } if (!String.IsNullOrEmpty(alt)) { imageTag.MergeAttribute("alt", alt); } imageTag.MergeAttributes(htmlAttributes, true); if (imageTag.Attributes.ContainsKey("alt") && !imageTag.Attributes.ContainsKey("title")) { imageTag.MergeAttribute("title", (imageTag.Attributes["alt"] ?? String.Empty)); } return imageTag; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Internal/ExpressionHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Internal { public static class ExpressionHelper { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")] public static RouteValueDictionary GetRouteValuesFromExpression(Expression> action) where TController : Controller { if (action == null) { throw new ArgumentNullException("action"); } MethodCallExpression call = action.Body as MethodCallExpression; if (call == null) { throw new ArgumentException(MvcResources.ExpressionHelper_MustBeMethodCall, "action"); } string controllerName = typeof(TController).Name; if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(MvcResources.ExpressionHelper_TargetMustEndInController, "action"); } controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length); if (controllerName.Length == 0) { throw new ArgumentException(MvcResources.ExpressionHelper_CannotRouteToController, "action"); } // TODO: How do we know that this method is even web callable? // For now, we just let the call itself throw an exception. string actionName = GetTargetActionName(call.Method); var rvd = new RouteValueDictionary(); rvd.Add("Controller", controllerName); rvd.Add("Action", actionName); ActionLinkAreaAttribute areaAttr = typeof(TController).GetCustomAttributes(typeof(ActionLinkAreaAttribute), true /* inherit */).FirstOrDefault() as ActionLinkAreaAttribute; if (areaAttr != null) { string areaName = areaAttr.Area; rvd.Add("Area", areaName); } AddParameterValuesFromExpressionToDictionary(rvd, call); return rvd; } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")] public static string GetInputName(Expression> expression) { if (expression.Body.NodeType == ExpressionType.Call) { MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body; string name = GetInputName(methodCallExpression); return name.Substring(expression.Parameters[0].Name.Length + 1); } return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1); } private static string GetInputName(MethodCallExpression expression) { // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw... MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression; if (methodCallExpression != null) { return GetInputName(methodCallExpression); } return expression.Object.ToString(); } // This method contains some heuristics that will help determine the correct action name from a given MethodInfo // assuming the default sync / async invokers are in use. The logic's not foolproof, but it should be good enough // for most uses. private static string GetTargetActionName(MethodInfo methodInfo) { string methodName = methodInfo.Name; // do we know this not to be an action? if (methodInfo.IsDefined(typeof(NonActionAttribute), true /* inherit */)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.ExpressionHelper_CannotCallNonAction, methodName)); } // has this been renamed? ActionNameAttribute nameAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), true /* inherit */).OfType().FirstOrDefault(); if (nameAttr != null) { return nameAttr.Name; } // targeting an async action? if (methodInfo.DeclaringType.IsSubclassOf(typeof(AsyncController))) { if (methodName.EndsWith("Async", StringComparison.OrdinalIgnoreCase)) { return methodName.Substring(0, methodName.Length - "Async".Length); } if (methodName.EndsWith("Completed", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.ExpressionHelper_CannotCallCompletedMethod, methodName)); } } // fallback return methodName; } private static void AddParameterValuesFromExpressionToDictionary(RouteValueDictionary rvd, MethodCallExpression call) { ParameterInfo[] parameters = call.Method.GetParameters(); if (parameters.Length > 0) { for (int i = 0; i < parameters.Length; i++) { Expression arg = call.Arguments[i]; object value = null; ConstantExpression ce = arg as ConstantExpression; if (ce != null) { // If argument is a constant expression, just get the value value = ce.Value; } else { value = CachedExpressionCompiler.Evaluate(arg); } rvd.Add(parameters[i].Name, value); } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/LinkBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Web.Mvc; using System.Web.Routing; using ExpressionHelper = Microsoft.Web.Mvc.Internal.ExpressionHelper; namespace Microsoft.Web.Mvc { public static class LinkBuilder { /// /// Builds a URL based on the Expression passed in /// /// Controller Type Only /// The current ViewContext /// The to use for building the URL. /// The action to invoke /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters"), SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an Extension Method which allows the user to provide a strongly-typed argument via Expression"), SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Need to be sure the passed-in argument is of type Controller::Action")] public static string BuildUrlFromExpression(RequestContext context, RouteCollection routeCollection, Expression> action) where TController : Controller { RouteValueDictionary routeValues = ExpressionHelper.GetRouteValuesFromExpression(action); VirtualPathData vpd = routeCollection.GetVirtualPathForArea(context, routeValues); return (vpd == null) ? null : vpd.VirtualPath; } /// /// Creates a querystring as a Dictionary based on the passed-in Lambda /// /// The Lambda of the Controller method /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Allowing Lambda compilation to fail if it doesn't compile at run time - design-time compilation will not allow for runtime Exception")] public static RouteValueDictionary BuildParameterValuesFromExpression(MethodCallExpression call) { RouteValueDictionary result = new RouteValueDictionary(); ParameterInfo[] parameters = call.Method.GetParameters(); if (parameters.Length > 0) { for (int i = 0; i < parameters.Length; i++) { Expression arg = call.Arguments[i]; object value; ConstantExpression ce = arg as ConstantExpression; if (ce != null) { // If argument is a constant expression, just get the value value = ce.Value; } else { try { value = CachedExpressionCompiler.Evaluate(arg); } catch { // ????? value = String.Empty; } } // Code should be added here to appropriately escape the value string result.Add(parameters[i].Name, value); } } return result; } } } ================================================ FILE: src/Microsoft.Web.Mvc/LinkExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; using ExpressionHelper = Microsoft.Web.Mvc.Internal.ExpressionHelper; namespace Microsoft.Web.Mvc { public static class LinkExtensions { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "This is a UI method and is required to use strings as Uri"), SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an Extension Method which allows the user to provide a strongly-typed argument via Expression")] public static string BuildUrlFromExpression(this HtmlHelper helper, Expression> action) where TController : Controller { return LinkBuilder.BuildUrlFromExpression(helper.ViewContext.RequestContext, helper.RouteCollection, action); } /// /// Creates an anchor tag based on the passed in controller type and method /// /// The Controller Type /// The where to create the link. /// The Method to route to /// The linked text to appear on the page /// The anchor tag. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcHtmlString ActionLink(this HtmlHelper helper, Expression> action, string linkText) where TController : Controller { return ActionLink(helper, action, linkText, null); } /// /// Creates an anchor tag based on the passed in controller type and method /// /// The Controller Type /// The where to create the link. /// The Method to route to /// The linked text to appear on the page /// Any additional HTML attributes. /// The anchor tag. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static MvcHtmlString ActionLink(this HtmlHelper helper, Expression> action, string linkText, object htmlAttributes) where TController : Controller { RouteValueDictionary routingValues = ExpressionHelper.GetRouteValuesFromExpression(action); return helper.RouteLink(linkText, routingValues, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/MachineKeyWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web; using System.Web.Security; namespace Microsoft.Web.Mvc { // Concrete implementation of IMachineKey that talks to the static MachineKey type internal sealed class MachineKeyWrapper : IMachineKey { private static readonly MachineKeyWrapper _singletonInstance = new MachineKeyWrapper(); public static MachineKeyWrapper Instance { get { return _singletonInstance; } } public byte[] Unprotect(string protectedData, params string[] purposes) { byte[] protectedBytes = Convert.FromBase64String(protectedData); return MachineKey.Unprotect(protectedBytes, purposes); } public string Protect(byte[] userData, params string[] purposes) { byte[] protectedBytes = MachineKey.Protect(userData, purposes); return Convert.ToBase64String(protectedBytes); } } } ================================================ FILE: src/Microsoft.Web.Mvc/MailToExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc { [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "MailTo", Justification = "This is correctly cased.")] public static class MailToExtensions { public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress) { return Mailto(helper, linkText, emailAddress, null, null, null, null, null); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, object htmlAttributes) { return Mailto(helper, linkText, emailAddress, null, null, null, null, htmlAttributes); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, IDictionary htmlAttributes) { return Mailto(helper, linkText, emailAddress, null, null, null, null, htmlAttributes); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, string subject) { return Mailto(helper, linkText, emailAddress, subject, null, null, null, null); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, string subject, object htmlAttributes) { return Mailto(helper, linkText, emailAddress, subject, null, null, null, htmlAttributes); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, string subject, IDictionary htmlAttributes) { return Mailto(helper, linkText, emailAddress, subject, null, null, null, htmlAttributes); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, string subject, string body, string cc, string bcc, object htmlAttributes) { return Mailto(helper, linkText, emailAddress, subject, body, cc, bcc, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } public static MvcHtmlString Mailto(this HtmlHelper helper, string linkText, string emailAddress, string subject, string body, string cc, string bcc, IDictionary htmlAttributes) { if (emailAddress == null) { throw new ArgumentNullException("emailAddress"); // TODO: Resource message } if (linkText == null) { throw new ArgumentNullException("linkText"); // TODO: Resource message } string mailToUrl = "mailto:" + emailAddress; List mailQuery = new List(); if (!String.IsNullOrEmpty(subject)) { mailQuery.Add("subject=" + helper.Encode(subject)); } if (!String.IsNullOrEmpty(cc)) { mailQuery.Add("cc=" + helper.Encode(cc)); } if (!String.IsNullOrEmpty(bcc)) { mailQuery.Add("bcc=" + helper.Encode(bcc)); } if (!String.IsNullOrEmpty(body)) { string encodedBody = helper.Encode(body); encodedBody = encodedBody.Replace(Environment.NewLine, "%0A"); mailQuery.Add("body=" + encodedBody); } string query = String.Empty; for (int i = 0; i < mailQuery.Count; i++) { query += mailQuery[i]; if (i < mailQuery.Count - 1) { query += "&"; } } if (query.Length > 0) { mailToUrl += "?" + query; } TagBuilder mailtoAnchor = new TagBuilder("a"); mailtoAnchor.MergeAttribute("href", mailToUrl); mailtoAnchor.MergeAttributes(htmlAttributes, true); mailtoAnchor.InnerHtml = linkText; return MvcHtmlString.Create(mailtoAnchor.ToString()); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Microsoft.Web.Mvc.csproj ================================================  {D3CF7430-6DA4-42B0-BD90-CA39D16687B2} Library Properties Microsoft.Web.Mvc Microsoft.Web.Mvc $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETMVC 1591 false Properties\CommonAssemblyInfo.cs ASPXCodeBehind ASPXCodeBehind True True MvcResources.resx {3D3FFD8A-624D-4E9B-954B-E1C105507975} System.Web.Mvc {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages ResXFileCodeGenerator MvcResources.Designer.cs Designer ASPXCodeBehind ASPXCodeBehind ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ArrayModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public class ArrayModelBinder : CollectionModelBinder { protected override bool CreateOrReplaceCollection(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, IList newCollection) { bindingContext.Model = newCollection.ToArray(); return true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ArrayModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ArrayModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); if (!bindingContext.ModelMetadata.IsReadOnly && bindingContext.ModelType.IsArray && bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { Type elementType = bindingContext.ModelType.GetElementType(); return (IExtensibleModelBinder)Activator.CreateInstance(typeof(ArrayModelBinder<>).MakeGenericType(elementType)); } return null; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/BinaryDataModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Data.Linq; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // This is a single provider that can work with both byte[] and Binary models. public sealed class BinaryDataModelBinderProvider : ModelBinderProvider { private static readonly ModelBinderProvider[] _providers = new ModelBinderProvider[] { new SimpleModelBinderProvider(typeof(byte[]), new ByteArrayExtensibleModelBinder()), new SimpleModelBinderProvider(typeof(Binary), new LinqBinaryExtensibleModelBinder()) }; public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return (from provider in _providers let binder = provider.GetBinder(controllerContext, bindingContext) where binder != null select binder).FirstOrDefault(); } // This is essentially a clone of the ByteArrayModelBinder from core private class ByteArrayExtensibleModelBinder : IExtensibleModelBinder { [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to ignore when the data is corrupted")] [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)", Justification = "The target object should make the correct culture determination, not this method.")] public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); // case 1: there was no element containing this data if (valueProviderResult == null) { return false; } string base64String = (string)valueProviderResult.ConvertTo(typeof(string)); // case 2: there was an element but it was left blank if (String.IsNullOrEmpty(base64String)) { return false; } // Future proofing. If the byte array is actually an instance of System.Data.Linq.Binary // then we need to remove these quotes put in place by the ToString() method. string realValue = base64String.Replace("\"", String.Empty); try { bindingContext.Model = ConvertByteArray(Convert.FromBase64String(realValue)); return true; } catch { // corrupt data - just ignore return false; } } protected virtual object ConvertByteArray(byte[] originalModel) { return originalModel; } } // This is essentially a clone of the LinqBinaryModelBinder from core private class LinqBinaryExtensibleModelBinder : ByteArrayExtensibleModelBinder { protected override object ConvertByteArray(byte[] originalModel) { return new Binary(originalModel); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/BindNeverAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc.ModelBinding { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class BindNeverAttribute : BindingBehaviorAttribute { public BindNeverAttribute() : base(BindingBehavior.Never) { } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/BindRequiredAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc.ModelBinding { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class BindRequiredAttribute : BindingBehaviorAttribute { public BindRequiredAttribute() : base(BindingBehavior.Required) { } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/BindingBehavior.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.ModelBinding { public enum BindingBehavior { Optional = 0, Never, Required } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/BindingBehaviorAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; namespace Microsoft.Web.Mvc.ModelBinding { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This class is designed to be overridden")] public class BindingBehaviorAttribute : Attribute { private static readonly object _typeId = new object(); public BindingBehaviorAttribute(BindingBehavior behavior) { Behavior = behavior; } public BindingBehavior Behavior { get; private set; } public override object TypeId { get { return _typeId; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/CollectionModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public class CollectionModelBinder : IExtensibleModelBinder { // Used when the ValueProvider contains the collection to be bound as multiple elements, e.g. foo[0], foo[1]. private static List BindComplexCollection(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { string indexPropertyName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, "index"); ValueProviderResult valueProviderResultIndex = bindingContext.ValueProvider.GetValue(indexPropertyName); IEnumerable indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(valueProviderResultIndex); return BindComplexCollectionFromIndexes(controllerContext, bindingContext, indexNames); } internal static List BindComplexCollectionFromIndexes(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, IEnumerable indexNames) { bool indexNamesIsFinite; if (indexNames != null) { indexNamesIsFinite = true; } else { indexNamesIsFinite = false; indexNames = CollectionModelBinderUtil.GetZeroBasedIndexes(); } List boundCollection = new List(); foreach (string indexName in indexNames) { string fullChildName = ModelBinderUtil.CreateIndexModelName(bindingContext.ModelName, indexName); ExtensibleModelBindingContext childBindingContext = new ExtensibleModelBindingContext(bindingContext) { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TElement)), ModelName = fullChildName }; object boundValue = null; IExtensibleModelBinder childBinder = bindingContext.ModelBinderProviders.GetBinder(controllerContext, childBindingContext); if (childBinder != null) { if (childBinder.BindModel(controllerContext, childBindingContext)) { boundValue = childBindingContext.Model; // merge validation up bindingContext.ValidationNode.ChildNodes.Add(childBindingContext.ValidationNode); } } else { // should we even bother continuing? if (!indexNamesIsFinite) { break; } } boundCollection.Add(ModelBinderUtil.CastOrDefault(boundValue)); } return boundCollection; } public virtual bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); List boundCollection = (valueProviderResult != null) ? BindSimpleCollection(controllerContext, bindingContext, valueProviderResult.RawValue, valueProviderResult.Culture) : BindComplexCollection(controllerContext, bindingContext); bool retVal = CreateOrReplaceCollection(controllerContext, bindingContext, boundCollection); return retVal; } // Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // is [ "1", "2" ] and needs to be converted to an int[]. internal static List BindSimpleCollection(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, object rawValue, CultureInfo culture) { if (rawValue == null) { return null; // nothing to do } List boundCollection = new List(); object[] rawValueArray = ModelBinderUtil.RawValueToObjectArray(rawValue); foreach (object rawValueElement in rawValueArray) { ExtensibleModelBindingContext innerBindingContext = new ExtensibleModelBindingContext(bindingContext) { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TElement)), ModelName = bindingContext.ModelName, ValueProvider = new ValueProviderCollection { // aggregate value provider new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture), // our temporary provider goes at the front of the list bindingContext.ValueProvider } }; object boundValue = null; IExtensibleModelBinder childBinder = bindingContext.ModelBinderProviders.GetBinder(controllerContext, innerBindingContext); if (childBinder != null) { if (childBinder.BindModel(controllerContext, innerBindingContext)) { boundValue = innerBindingContext.Model; bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode); } } boundCollection.Add(ModelBinderUtil.CastOrDefault(boundValue)); } return boundCollection; } // Extensibility point that allows the bound collection to be manipulated or transformed before // being returned from the binder. protected virtual bool CreateOrReplaceCollection(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, IList newCollection) { CollectionModelBinderUtil.CreateOrReplaceCollection(bindingContext, newCollection, () => new List()); return true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/CollectionModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class CollectionModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return CollectionModelBinderUtil.GetGenericBinder(typeof(ICollection<>), typeof(List<>), typeof(CollectionModelBinder<>), bindingContext.ModelMetadata); } else { return null; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/CollectionModelBinderUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { internal static class CollectionModelBinderUtil { public static void CreateOrReplaceCollection(ExtensibleModelBindingContext bindingContext, IEnumerable incomingElements, Func> creator) { ICollection collection = bindingContext.Model as ICollection; if (collection == null || collection.IsReadOnly) { collection = creator(); bindingContext.Model = collection; } collection.Clear(); foreach (TElement element in incomingElements) { collection.Add(element); } } public static void CreateOrReplaceDictionary(ExtensibleModelBindingContext bindingContext, IEnumerable> incomingElements, Func> creator) { IDictionary dictionary = bindingContext.Model as IDictionary; if (dictionary == null || dictionary.IsReadOnly) { dictionary = creator(); bindingContext.Model = dictionary; } dictionary.Clear(); foreach (var element in incomingElements) { if (element.Key != null) { dictionary[element.Key] = element.Value; } } } // supportedInterfaceType: type that is updatable by this binder // newInstanceType: type that will be created by the binder if necessary // openBinderType: model binder type // modelMetadata: metadata for the model to bind // // example: GetGenericBinder(typeof(IList<>), typeof(List<>), typeof(ListBinder<>), ...) means that the ListBinder // type can update models that implement IList, and if for some reason the existing model instance is not // updatable the binder will create a List object and bind to that instead. This method will return a ListBinder // or null, depending on whether the type and updatability checks succeed. public static IExtensibleModelBinder GetGenericBinder(Type supportedInterfaceType, Type newInstanceType, Type openBinderType, ModelMetadata modelMetadata) { Type[] typeArguments = GetTypeArgumentsForUpdatableGenericCollection(supportedInterfaceType, newInstanceType, modelMetadata); return (typeArguments != null) ? (IExtensibleModelBinder)Activator.CreateInstance(openBinderType.MakeGenericType(typeArguments)) : null; } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)", Justification = "This model binder binds collections, so it does not need culture.")] public static IEnumerable GetIndexNamesFromValueProviderResult(ValueProviderResult valueProviderResultIndex) { IEnumerable indexNames = null; if (valueProviderResultIndex != null) { string[] indexes = (string[])(valueProviderResultIndex.ConvertTo(typeof(string[]))); if (indexes != null && indexes.Length > 0) { indexNames = indexes; } } return indexNames; } public static IEnumerable GetZeroBasedIndexes() { int i = 0; while (true) { yield return i.ToString(CultureInfo.InvariantCulture); i++; } } // Returns the generic type arguments for the model type if updatable, else null. // supportedInterfaceType: open type (like IList<>) of supported interface, must implement ICollection<> // newInstanceType: open type (like List<>) of object that will be created, must implement supportedInterfaceType public static Type[] GetTypeArgumentsForUpdatableGenericCollection(Type supportedInterfaceType, Type newInstanceType, ModelMetadata modelMetadata) { /* * Check that we can extract proper type arguments from the model. */ if (!modelMetadata.ModelType.IsGenericType || modelMetadata.ModelType.IsGenericTypeDefinition) { // not a closed generic type return null; } Type[] modelTypeArguments = modelMetadata.ModelType.GetGenericArguments(); if (modelTypeArguments.Length != supportedInterfaceType.GetGenericArguments().Length) { // wrong number of generic type arguments return null; } /* * Is it possible just to change the reference rather than update the collection in-place? */ if (!modelMetadata.IsReadOnly) { Type closedNewInstanceType = newInstanceType.MakeGenericType(modelTypeArguments); if (modelMetadata.ModelType.IsAssignableFrom(closedNewInstanceType)) { return modelTypeArguments; } } /* * At this point, we know we can't change the reference, so we need to verify that * the model instance can be updated in-place. */ Type closedSupportedInterfaceType = supportedInterfaceType.MakeGenericType(modelTypeArguments); if (!closedSupportedInterfaceType.IsInstanceOfType(modelMetadata.Model)) { return null; // not instance of correct interface } Type closedCollectionType = TypeHelpers.ExtractGenericInterface(closedSupportedInterfaceType, typeof(ICollection<>)); bool collectionInstanceIsReadOnly = (bool)closedCollectionType.GetProperty("IsReadOnly").GetValue(modelMetadata.Model, null); if (collectionInstanceIsReadOnly) { return null; } else { return modelTypeArguments; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ComplexModelDto.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Describes a complex model, but uses a collection rather than individual properties as the data store. public class ComplexModelDto { public ComplexModelDto(ModelMetadata modelMetadata, IEnumerable propertyMetadata) { if (modelMetadata == null) { throw new ArgumentNullException("modelMetadata"); } if (propertyMetadata == null) { throw new ArgumentNullException("propertyMetadata"); } ModelMetadata = modelMetadata; PropertyMetadata = new ReadOnlyCollection(propertyMetadata.ToList()); Results = new Dictionary(); } public ModelMetadata ModelMetadata { get; private set; } public ReadOnlyCollection PropertyMetadata { get; private set; } // Contains entries corresponding to each property against which binding was // attempted. If binding failed, the entry's value will be null. If binding // was never attempted, this dictionary will not contain a corresponding // entry. public IDictionary Results { get; private set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ComplexModelDtoModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ComplexModelDtoModelBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(ComplexModelDto), false /* allowNullModel */); ComplexModelDto dto = (ComplexModelDto)bindingContext.Model; foreach (ModelMetadata propertyMetadata in dto.PropertyMetadata) { ExtensibleModelBindingContext propertyBindingContext = new ExtensibleModelBindingContext(bindingContext) { ModelMetadata = propertyMetadata, ModelName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, propertyMetadata.PropertyName) }; // bind and propagate the values IExtensibleModelBinder propertyBinder = bindingContext.ModelBinderProviders.GetBinder(controllerContext, propertyBindingContext); if (propertyBinder != null) { if (propertyBinder.BindModel(controllerContext, propertyBindingContext)) { dto.Results[propertyMetadata] = new ComplexModelDtoResult(propertyBindingContext.Model, propertyBindingContext.ValidationNode); } else { dto.Results[propertyMetadata] = null; } } } return true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ComplexModelDtoModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Returns a binder that can bind ComplexModelDto objects. public sealed class ComplexModelDtoModelBinderProvider : ModelBinderProvider { // This is really just a simple binder. private static readonly SimpleModelBinderProvider _underlyingProvider = GetUnderlyingProvider(); public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return _underlyingProvider.GetBinder(controllerContext, bindingContext); } private static SimpleModelBinderProvider GetUnderlyingProvider() { return new SimpleModelBinderProvider(typeof(ComplexModelDto), new ComplexModelDtoModelBinder()) { SuppressPrefixCheck = true }; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ComplexModelDtoResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ComplexModelDtoResult { public ComplexModelDtoResult(object model, ModelValidationNode validationNode) { if (validationNode == null) { throw new ArgumentNullException("validationNode"); } Model = model; ValidationNode = validationNode; } public object Model { get; private set; } public ModelValidationNode ValidationNode { get; private set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/DictionaryModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public class DictionaryModelBinder : CollectionModelBinder> { protected override bool CreateOrReplaceCollection(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, IList> newCollection) { CollectionModelBinderUtil.CreateOrReplaceDictionary(bindingContext, newCollection, () => new Dictionary()); return true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/DictionaryModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class DictionaryModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); if (bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return CollectionModelBinderUtil.GetGenericBinder(typeof(IDictionary<,>), typeof(Dictionary<,>), typeof(DictionaryModelBinder<,>), bindingContext.ModelMetadata); } else { return null; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ExtensibleModelBinderAdapter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.ModelBinding { // A model binder that is used to interface between the old system and the new system. public sealed class ExtensibleModelBinderAdapter : IModelBinder { public ExtensibleModelBinderAdapter(ModelBinderProviderCollection providers) { Providers = providers ?? ModelBinderProviders.Providers; } public ModelBinderProviderCollection Providers { get; private set; } public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { CheckPropertyFilter(bindingContext); ExtensibleModelBindingContext newBindingContext = CreateNewBindingContext(bindingContext, bindingContext.ModelName); IExtensibleModelBinder binder = Providers.GetBinder(controllerContext, newBindingContext); if (binder == null && !String.IsNullOrEmpty(bindingContext.ModelName) && bindingContext.FallbackToEmptyPrefix && bindingContext.ModelMetadata.IsComplexType) { // fallback to empty prefix? newBindingContext = CreateNewBindingContext(bindingContext, String.Empty /* modelName */); binder = Providers.GetBinder(controllerContext, newBindingContext); } if (binder != null) { bool boundSuccessfully = binder.BindModel(controllerContext, newBindingContext); if (boundSuccessfully) { // run validation and return the model newBindingContext.ValidationNode.Validate(controllerContext, null /* parentNode */); return newBindingContext.Model; } } return null; // something went wrong } private static void CheckPropertyFilter(ModelBindingContext bindingContext) { if (bindingContext.ModelType.GetProperties().Select(p => p.Name).Any(name => !bindingContext.PropertyFilter(name))) { throw new InvalidOperationException(MvcResources.ExtensibleModelBinderAdapter_PropertyFilterMustNotBeSet); } } private ExtensibleModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext, string modelName) { ExtensibleModelBindingContext newBindingContext = new ExtensibleModelBindingContext { ModelBinderProviders = Providers, ModelMetadata = oldBindingContext.ModelMetadata, ModelName = modelName, ModelState = oldBindingContext.ModelState, ValueProvider = oldBindingContext.ValueProvider }; return newBindingContext; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ExtensibleModelBinderAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc.ModelBinding { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] public sealed class ExtensibleModelBinderAttribute : Attribute { public ExtensibleModelBinderAttribute(Type binderType) { BinderType = binderType; } public Type BinderType { get; private set; } public bool SuppressPrefixCheck { get; set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ExtensibleModelBindingContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public class ExtensibleModelBindingContext { private ModelBinderProviderCollection _modelBinderProviders; private string _modelName; private ModelStateDictionary _modelState; private Dictionary _propertyMetadata; private ModelValidationNode _validationNode; public ExtensibleModelBindingContext() : this(null) { } // copies certain values that won't change between parent and child objects, // e.g. ValueProvider, ModelState public ExtensibleModelBindingContext(ExtensibleModelBindingContext bindingContext) { if (bindingContext != null) { ModelBinderProviders = bindingContext.ModelBinderProviders; ModelState = bindingContext.ModelState; ValueProvider = bindingContext.ValueProvider; } } public object Model { get { EnsureModelMetadata(); return ModelMetadata.Model; } set { EnsureModelMetadata(); ModelMetadata.Model = value; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is writeable to support unit testing")] public ModelBinderProviderCollection ModelBinderProviders { get { if (_modelBinderProviders == null) { _modelBinderProviders = ModelBinding.ModelBinderProviders.Providers; } return _modelBinderProviders; } set { _modelBinderProviders = value; } } public ModelMetadata ModelMetadata { get; set; } public string ModelName { get { if (_modelName == null) { _modelName = String.Empty; } return _modelName; } set { _modelName = value; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is writeable to support unit testing")] public ModelStateDictionary ModelState { get { if (_modelState == null) { _modelState = new ModelStateDictionary(); } return _modelState; } set { _modelState = value; } } public Type ModelType { get { EnsureModelMetadata(); return ModelMetadata.ModelType; } } public IDictionary PropertyMetadata { get { if (_propertyMetadata == null) { _propertyMetadata = ModelMetadata.Properties.ToDictionary(m => m.PropertyName, StringComparer.OrdinalIgnoreCase); } return _propertyMetadata; } } public ModelValidationNode ValidationNode { get { if (_validationNode == null) { _validationNode = new ModelValidationNode(ModelMetadata, ModelName); } return _validationNode; } set { _validationNode = value; } } public IValueProvider ValueProvider { get; set; } private void EnsureModelMetadata() { if (ModelMetadata == null) { throw Error.ModelBindingContext_ModelMetadataMustBeSet(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/GenericModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Returns a user-specified binder for a given open generic type. public sealed class GenericModelBinderProvider : ModelBinderProvider { private readonly Func _modelBinderFactory; private readonly Type _modelType; public GenericModelBinderProvider(Type modelType, IExtensibleModelBinder modelBinder) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinder == null) { throw new ArgumentNullException("modelBinder"); } ValidateParameters(modelType, null /* modelBinderType */); _modelType = modelType; _modelBinderFactory = _ => modelBinder; } public GenericModelBinderProvider(Type modelType, Type modelBinderType) { // The binder can be a closed type, in which case it will be instantiated directly. If the binder // is an open type, the type arguments will be determined at runtime and the corresponding closed // type instantiated. if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinderType == null) { throw new ArgumentNullException("modelBinderType"); } ValidateParameters(modelType, modelBinderType); bool modelBinderTypeIsOpenGeneric = modelBinderType.IsGenericTypeDefinition; _modelType = modelType; _modelBinderFactory = typeArguments => { Type closedModelBinderType = (modelBinderTypeIsOpenGeneric) ? modelBinderType.MakeGenericType(typeArguments) : modelBinderType; try { return (IExtensibleModelBinder)Activator.CreateInstance(closedModelBinderType); } catch (MissingMethodException exception) { // Ensure thrown exception contains the type name. Might be down a few levels. MissingMethodException replacementException = ModelBinderUtil.EnsureDebuggableException(exception, closedModelBinderType.FullName); if (replacementException != null) { throw replacementException; } throw; } }; } public GenericModelBinderProvider(Type modelType, Func modelBinderFactory) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinderFactory == null) { throw new ArgumentNullException("modelBinderFactory"); } ValidateParameters(modelType, null /* modelBinderType */); _modelType = modelType; _modelBinderFactory = modelBinderFactory; } public Type ModelType { get { return _modelType; } } public bool SuppressPrefixCheck { get; set; } public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); Type[] typeArguments = null; if (ModelType.IsInterface) { Type matchingClosedInterface = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, ModelType); if (matchingClosedInterface != null) { typeArguments = matchingClosedInterface.GetGenericArguments(); } } else { typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(bindingContext.ModelType, ModelType); } if (typeArguments != null) { if (SuppressPrefixCheck || bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return _modelBinderFactory(typeArguments); } } return null; } private static void ValidateParameters(Type modelType, Type modelBinderType) { if (!modelType.IsGenericTypeDefinition) { throw Error.GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType(modelType, "modelType"); } if (modelBinderType != null) { if (!typeof(IExtensibleModelBinder).IsAssignableFrom(modelBinderType)) { throw Error.Common_TypeMustImplementInterface(modelBinderType, typeof(IExtensibleModelBinder), "modelBinderType"); } if (modelBinderType.IsGenericTypeDefinition) { if (modelType.GetGenericArguments().Length != modelBinderType.GetGenericArguments().Length) { throw Error.GenericModelBinderProvider_TypeArgumentCountMismatch(modelType, modelBinderType); } } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/IExtensibleModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public interface IExtensibleModelBinder { bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext); } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/KeyValuePairModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class KeyValuePairModelBinder : IExtensibleModelBinder { private ModelMetadataProvider _metadataProvider; internal ModelMetadataProvider MetadataProvider { get { if (_metadataProvider == null) { _metadataProvider = ModelMetadataProviders.Current; } return _metadataProvider; } set { _metadataProvider = value; } } public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(KeyValuePair), true /* allowNullModel */); TKey key; bool keyBindingSucceeded = KeyValuePairModelBinderUtil.TryBindStrongModel(controllerContext, bindingContext, "key", MetadataProvider, out key); TValue value; bool valueBindingSucceeded = KeyValuePairModelBinderUtil.TryBindStrongModel(controllerContext, bindingContext, "value", MetadataProvider, out value); if (keyBindingSucceeded && valueBindingSucceeded) { bindingContext.Model = new KeyValuePair(key, value); } return keyBindingSucceeded || valueBindingSucceeded; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/KeyValuePairModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class KeyValuePairModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); string keyFieldName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, "key"); string valueFieldName = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, "value"); if (bindingContext.ValueProvider.ContainsPrefix(keyFieldName) && bindingContext.ValueProvider.ContainsPrefix(valueFieldName)) { return ModelBinderUtil.GetPossibleBinderInstance(bindingContext.ModelType, typeof(KeyValuePair<,>) /* supported model type */, typeof(KeyValuePairModelBinder<,>) /* binder type */); } else { // 'key' or 'value' missing return null; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/KeyValuePairModelBinderUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { internal static class KeyValuePairModelBinderUtil { public static bool TryBindStrongModel(ControllerContext controllerContext, ExtensibleModelBindingContext parentBindingContext, string propertyName, ModelMetadataProvider metadataProvider, out TModel model) { ExtensibleModelBindingContext propertyBindingContext = new ExtensibleModelBindingContext(parentBindingContext) { ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(TModel)), ModelName = ModelBinderUtil.CreatePropertyModelName(parentBindingContext.ModelName, propertyName) }; IExtensibleModelBinder binder = parentBindingContext.ModelBinderProviders.GetBinder(controllerContext, propertyBindingContext); if (binder != null) { if (binder.BindModel(controllerContext, propertyBindingContext)) { object untypedModel = propertyBindingContext.Model; model = ModelBinderUtil.CastOrDefault(untypedModel); parentBindingContext.ValidationNode.ChildNodes.Add(propertyBindingContext.ValidationNode); return true; } } model = default(TModel); return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderConfig.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.ModelBinding { // Provides configuration settings common to the new model binding system. public static class ModelBinderConfig { private static ModelBinderErrorMessageProvider _typeConversionErrorMessageProvider; private static ModelBinderErrorMessageProvider _valueRequiredErrorMessageProvider; public static ModelBinderErrorMessageProvider TypeConversionErrorMessageProvider { get { if (_typeConversionErrorMessageProvider == null) { _typeConversionErrorMessageProvider = DefaultTypeConversionErrorMessageProvider; } return _typeConversionErrorMessageProvider; } set { _typeConversionErrorMessageProvider = value; } } public static ModelBinderErrorMessageProvider ValueRequiredErrorMessageProvider { get { if (_valueRequiredErrorMessageProvider == null) { _valueRequiredErrorMessageProvider = DefaultValueRequiredErrorMessageProvider; } return _valueRequiredErrorMessageProvider; } set { _valueRequiredErrorMessageProvider = value; } } private static string DefaultTypeConversionErrorMessageProvider(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue) { return GetResourceCommon(controllerContext, modelMetadata, incomingValue, GetValueInvalidResource); } private static string DefaultValueRequiredErrorMessageProvider(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue) { return GetResourceCommon(controllerContext, modelMetadata, incomingValue, GetValueRequiredResource); } private static string GetResourceCommon(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue, Func resourceAccessor) { string displayName = modelMetadata.GetDisplayName(); string errorMessageTemplate = resourceAccessor(controllerContext); string errorMessage = String.Format(CultureInfo.CurrentCulture, errorMessageTemplate, incomingValue, displayName); return errorMessage; } private static string GetUserResourceString(ControllerContext controllerContext, string resourceName) { return GetUserResourceString(controllerContext, resourceName, DefaultModelBinder.ResourceClassKey); } // If the user specified a ResourceClassKey try to load the resource they specified. // If the class key is invalid, an exception will be thrown. // If the class key is valid but the resource is not found, it returns null, in which // case it will fall back to the MVC default error message. internal static string GetUserResourceString(ControllerContext controllerContext, string resourceName, string resourceClassKey) { return (!String.IsNullOrEmpty(resourceClassKey) && (controllerContext != null) && (controllerContext.HttpContext != null)) ? controllerContext.HttpContext.GetGlobalResourceObject(resourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string : null; } private static string GetValueInvalidResource(ControllerContext controllerContext) { return GetUserResourceString(controllerContext, "PropertyValueInvalid") ?? MvcResources.ModelBinderConfig_ValueInvalid; } private static string GetValueRequiredResource(ControllerContext controllerContext) { return GetUserResourceString(controllerContext, "PropertyValueRequired") ?? MvcResources.ModelBinderConfig_ValueRequired; } /* * Initialization routines which replace the default binder implementation with the new binder implementation. */ public static void Initialize() { Initialize(ModelBinders.Binders, ModelBinderProviders.Providers); } internal static void Initialize(ModelBinderDictionary binders, ModelBinderProviderCollection providers) { binders.Clear(); binders.DefaultBinder = new ExtensibleModelBinderAdapter(providers); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderErrorMessageProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public delegate string ModelBinderErrorMessageProvider(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue); } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public abstract class ModelBinderProvider { public abstract IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext); } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderProviderCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ModelBinderProviderCollection : Collection { public ModelBinderProviderCollection() { } public ModelBinderProviderCollection(IList list) : base(list) { } private static void EnsureNoBindAttribute(Type modelType) { if (TypeDescriptorHelper.Get(modelType).GetAttributes().OfType().Any()) { string errorMessage = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderProviderCollection_TypeCannotHaveBindAttribute, modelType); throw new InvalidOperationException(errorMessage); } } public IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } EnsureNoBindAttribute(bindingContext.ModelType); ModelBinderProvider providerFromAttr; if (TryGetProviderFromAttributes(bindingContext.ModelType, out providerFromAttr)) { return providerFromAttr.GetBinder(controllerContext, bindingContext); } return (from provider in this let binder = provider.GetBinder(controllerContext, bindingContext) where binder != null select binder).FirstOrDefault(); } internal IExtensibleModelBinder GetRequiredBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { IExtensibleModelBinder binder = GetBinder(controllerContext, bindingContext); if (binder == null) { throw Error.ModelBinderProviderCollection_BinderForTypeNotFound(bindingContext.ModelType); } return binder; } protected override void InsertItem(int index, ModelBinderProvider item) { if (item == null) { throw new ArgumentNullException("item"); } base.InsertItem(index, item); } private void InsertSimpleProviderAtFront(ModelBinderProvider provider) { // Don't want to insert simple providers before any that are marked as "should go first," // as that might throw off other providers like the exact type match provider. int i = 0; for (; i < Count; i++) { if (!ShouldProviderGoFirst(this[i])) { break; } } base.InsertItem(i, provider); } public void RegisterBinderForGenericType(Type modelType, IExtensibleModelBinder modelBinder) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinder)); } public void RegisterBinderForGenericType(Type modelType, Func modelBinderFactory) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderFactory)); } public void RegisterBinderForGenericType(Type modelType, Type modelBinderType) { InsertSimpleProviderAtFront(new GenericModelBinderProvider(modelType, modelBinderType)); } public void RegisterBinderForType(Type modelType, IExtensibleModelBinder modelBinder) { RegisterBinderForType(modelType, modelBinder, false /* suppressPrefixCheck */); } internal void RegisterBinderForType(Type modelType, IExtensibleModelBinder modelBinder, bool suppressPrefixCheck) { SimpleModelBinderProvider provider = new SimpleModelBinderProvider(modelType, modelBinder) { SuppressPrefixCheck = suppressPrefixCheck }; InsertSimpleProviderAtFront(provider); } public void RegisterBinderForType(Type modelType, Func modelBinderFactory) { InsertSimpleProviderAtFront(new SimpleModelBinderProvider(modelType, modelBinderFactory)); } protected override void SetItem(int index, ModelBinderProvider item) { if (item == null) { throw new ArgumentNullException("item"); } base.SetItem(index, item); } private static bool ShouldProviderGoFirst(ModelBinderProvider provider) { ModelBinderProviderOptionsAttribute options = provider.GetType() .GetCustomAttributes(typeof(ModelBinderProviderOptionsAttribute), true /* inherit */) .OfType() .FirstOrDefault(); return (options != null) ? options.FrontOfList : false; } private static bool TryGetProviderFromAttributes(Type modelType, out ModelBinderProvider provider) { ExtensibleModelBinderAttribute attr = TypeDescriptorHelper.Get(modelType).GetAttributes().OfType().FirstOrDefault(); if (attr == null) { provider = null; return false; } if (typeof(ModelBinderProvider).IsAssignableFrom(attr.BinderType)) { provider = (ModelBinderProvider)CreateInstance(attr.BinderType); } else if (typeof(IExtensibleModelBinder).IsAssignableFrom(attr.BinderType)) { Type closedBinderType = (attr.BinderType.IsGenericTypeDefinition) ? attr.BinderType.MakeGenericType(modelType.GetGenericArguments()) : attr.BinderType; IExtensibleModelBinder binderInstance = (IExtensibleModelBinder)CreateInstance(closedBinderType); provider = new SimpleModelBinderProvider(modelType, binderInstance) { SuppressPrefixCheck = attr.SuppressPrefixCheck }; } else { string errorMessage = String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderProviderCollection_InvalidBinderType, attr.BinderType, typeof(ModelBinderProvider), typeof(IExtensibleModelBinder)); throw new InvalidOperationException(errorMessage); } return true; } private static object CreateInstance(Type type) { try { return Activator.CreateInstance(type); } catch (MissingMethodException exception) { // Ensure thrown exception contains the type name. Might be down a few levels. MissingMethodException replacementException = ModelBinderUtil.EnsureDebuggableException(exception, type.FullName); if (replacementException != null) { throw replacementException; } throw; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderProviderOptionsAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc.ModelBinding { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public sealed class ModelBinderProviderOptionsAttribute : Attribute { // Specifies that a provider should appear at the front of the list, e.g. other providers should // not be auto-registered at the front unless explicitly requested. public bool FrontOfList { get; set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderProviders.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.ModelBinding { public static class ModelBinderProviders { private static readonly ModelBinderProviderCollection _providers = CreateDefaultCollection(); public static ModelBinderProviderCollection Providers { get { return _providers; } } private static ModelBinderProviderCollection CreateDefaultCollection() { return new ModelBinderProviderCollection { new TypeMatchModelBinderProvider(), new BinaryDataModelBinderProvider(), new KeyValuePairModelBinderProvider(), new ComplexModelDtoModelBinderProvider(), new ArrayModelBinderProvider(), new DictionaryModelBinderProvider(), new CollectionModelBinderProvider(), new TypeConverterModelBinderProvider(), new MutableObjectModelBinderProvider() }; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelBinderUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Globalization; using System.Linq; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.ModelBinding { internal static class ModelBinderUtil { public static TModel CastOrDefault(object model) { return (model is TModel) ? (TModel)model : default(TModel); } public static string CreateIndexModelName(string parentName, int index) { return CreateIndexModelName(parentName, index.ToString(CultureInfo.InvariantCulture)); } public static string CreateIndexModelName(string parentName, string index) { return (parentName.Length == 0) ? "[" + index + "]" : parentName + "[" + index + "]"; } public static string CreatePropertyModelName(string prefix, string propertyName) { if (String.IsNullOrEmpty(prefix)) { return propertyName ?? String.Empty; } else if (String.IsNullOrEmpty(propertyName)) { return prefix ?? String.Empty; } else { return prefix + "." + propertyName; } } public static IExtensibleModelBinder GetPossibleBinderInstance(Type closedModelType, Type openModelType, Type openBinderType) { Type[] typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(closedModelType, openModelType); return (typeArguments != null) ? (IExtensibleModelBinder)Activator.CreateInstance(openBinderType.MakeGenericType(typeArguments)) : null; } public static object[] RawValueToObjectArray(object rawValue) { // precondition: rawValue is not null // Need to special-case String so it's not caught by the IEnumerable check which follows if (rawValue is string) { return new[] { rawValue }; } object[] rawValueAsObjectArray = rawValue as object[]; if (rawValueAsObjectArray != null) { return rawValueAsObjectArray; } IEnumerable rawValueAsEnumerable = rawValue as IEnumerable; if (rawValueAsEnumerable != null) { return rawValueAsEnumerable.Cast().ToArray(); } // fallback return new[] { rawValue }; } public static void ReplaceEmptyStringWithNull(ModelMetadata modelMetadata, ref object model) { if (modelMetadata.ConvertEmptyStringToNull && StringIsEmptyOrWhitespace(model as string)) { model = null; } } /// /// Provide a new if original Message does not contain given full Type name. /// /// to check. /// Full Type name which Message should contain. /// New if an update is required; null otherwise. public static MissingMethodException EnsureDebuggableException( MissingMethodException originalException, string fullTypeName) { MissingMethodException replacementException = null; if (!originalException.Message.Contains(fullTypeName)) { string message = String.Format( CultureInfo.CurrentCulture, MvcResources.ModelBinderUtil_CannotCreateInstance, originalException.Message, fullTypeName); replacementException = new MissingMethodException(message, originalException); } return replacementException; } // Based on String.IsNullOrWhitespace private static bool StringIsEmptyOrWhitespace(string s) { if (s == null) { return false; } if (s.Length != 0) { for (int i = 0; i < s.Length; i++) { if (!Char.IsWhiteSpace(s[i])) { return false; } } } return true; } public static void ValidateBindingContext(ExtensibleModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } if (bindingContext.ModelMetadata == null) { throw Error.ModelBinderUtil_ModelMetadataCannotBeNull(); } } public static void ValidateBindingContext(ExtensibleModelBindingContext bindingContext, Type requiredType, bool allowNullModel) { ValidateBindingContext(bindingContext); if (bindingContext.ModelType != requiredType) { throw Error.ModelBinderUtil_ModelTypeIsWrong(bindingContext.ModelType, requiredType); } if (!allowNullModel && bindingContext.Model == null) { throw Error.ModelBinderUtil_ModelCannotBeNull(requiredType); } if (bindingContext.Model != null && !requiredType.IsInstanceOfType(bindingContext.Model)) { throw Error.ModelBinderUtil_ModelInstanceIsWrong(bindingContext.Model.GetType(), requiredType); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelValidatedEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ModelValidatedEventArgs : EventArgs { public ModelValidatedEventArgs(ControllerContext controllerContext, ModelValidationNode parentNode) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } ControllerContext = controllerContext; ParentNode = parentNode; } public ControllerContext ControllerContext { get; private set; } public ModelValidationNode ParentNode { get; private set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelValidatingEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ModelValidatingEventArgs : CancelEventArgs { public ModelValidatingEventArgs(ControllerContext controllerContext, ModelValidationNode parentNode) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } ControllerContext = controllerContext; ParentNode = parentNode; } public ControllerContext ControllerContext { get; private set; } public ModelValidationNode ParentNode { get; private set; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/ModelValidationNode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class ModelValidationNode { public ModelValidationNode(ModelMetadata modelMetadata, string modelStateKey) : this(modelMetadata, modelStateKey, null) { } public ModelValidationNode(ModelMetadata modelMetadata, string modelStateKey, IEnumerable childNodes) { if (modelMetadata == null) { throw new ArgumentNullException("modelMetadata"); } if (modelStateKey == null) { throw new ArgumentNullException("modelStateKey"); } ModelMetadata = modelMetadata; ModelStateKey = modelStateKey; ChildNodes = (childNodes != null) ? childNodes.ToList() : new List(); } public event EventHandler Validated; public event EventHandler Validating; public ICollection ChildNodes { get; private set; } public ModelMetadata ModelMetadata { get; private set; } public string ModelStateKey { get; private set; } public bool ValidateAllProperties { get; set; } public bool SuppressValidation { get; set; } public void CombineWith(ModelValidationNode otherNode) { if (otherNode != null && !otherNode.SuppressValidation) { Validated += otherNode.Validated; Validating += otherNode.Validating; foreach (ModelValidationNode childNode in otherNode.ChildNodes) { ChildNodes.Add(childNode); } } } private void OnValidated(ModelValidatedEventArgs e) { EventHandler handler = Validated; if (handler != null) { handler(this, e); } } private void OnValidating(ModelValidatingEventArgs e) { EventHandler handler = Validating; if (handler != null) { handler(this, e); } } private object TryConvertContainerToMetadataType(ModelValidationNode parentNode) { if (parentNode != null) { object containerInstance = parentNode.ModelMetadata.Model; if (containerInstance != null) { Type expectedContainerType = ModelMetadata.ContainerType; if (expectedContainerType != null) { if (expectedContainerType.IsInstanceOfType(containerInstance)) { return containerInstance; } } } } return null; } public void Validate(ControllerContext controllerContext) { Validate(controllerContext, null /* parentNode */); } public void Validate(ControllerContext controllerContext, ModelValidationNode parentNode) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (SuppressValidation) { // no-op return; } // pre-validation steps ModelValidatingEventArgs validatingEventArgs = new ModelValidatingEventArgs(controllerContext, parentNode); OnValidating(validatingEventArgs); if (validatingEventArgs.Cancel) { return; } ValidateChildren(controllerContext); ValidateThis(controllerContext, parentNode); // post-validation steps ModelValidatedEventArgs validatedEventArgs = new ModelValidatedEventArgs(controllerContext, parentNode); OnValidated(validatedEventArgs); } private void ValidateChildren(ControllerContext controllerContext) { foreach (ModelValidationNode child in ChildNodes) { child.Validate(controllerContext, this); } if (ValidateAllProperties) { ValidateProperties(controllerContext); } } private void ValidateProperties(ControllerContext controllerContext) { // Based off CompositeModelValidator. ModelStateDictionary modelState = controllerContext.Controller.ViewData.ModelState; // DevDiv Bugs #227802 - Caching problem in ModelMetadata requires us to manually regenerate // the ModelMetadata. object model = ModelMetadata.Model; ModelMetadata updatedMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, ModelMetadata.ModelType); foreach (ModelMetadata propertyMetadata in updatedMetadata.Properties) { // Only want to add errors to ModelState if something doesn't already exist for the property node, // else we could end up with duplicate or irrelevant error messages. string propertyKeyRoot = ModelBinderUtil.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName); if (modelState.IsValidField(propertyKeyRoot)) { foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(controllerContext)) { foreach (ModelValidationResult propertyResult in propertyValidator.Validate(model)) { string thisErrorKey = ModelBinderUtil.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName); modelState.AddModelError(thisErrorKey, propertyResult.Message); } } } } } private void ValidateThis(ControllerContext controllerContext, ModelValidationNode parentNode) { ModelStateDictionary modelState = controllerContext.Controller.ViewData.ModelState; if (!modelState.IsValidField(ModelStateKey)) { return; // short-circuit } object container = TryConvertContainerToMetadataType(parentNode); foreach (ModelValidator validator in ModelMetadata.GetValidators(controllerContext)) { foreach (ModelValidationResult validationResult in validator.Validate(container)) { string trueModelStateKey = ModelBinderUtil.CreatePropertyModelName(ModelStateKey, validationResult.MemberName); modelState.AddModelError(trueModelStateKey, validationResult.Message); } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/MutableObjectModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public class MutableObjectModelBinder : IExtensibleModelBinder { private ModelMetadataProvider _metadataProvider; internal ModelMetadataProvider MetadataProvider { get { if (_metadataProvider == null) { _metadataProvider = ModelMetadataProviders.Current; } return _metadataProvider; } set { _metadataProvider = value; } } public virtual bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); EnsureModel(controllerContext, bindingContext); IEnumerable propertyMetadatas = GetMetadataForProperties(controllerContext, bindingContext); ComplexModelDto dto = CreateAndPopulateDto(controllerContext, bindingContext, propertyMetadatas); // post-processing, e.g. property setters and hooking up validation ProcessDto(controllerContext, bindingContext, dto); bindingContext.ValidationNode.ValidateAllProperties = true; // complex models require full validation return true; } protected virtual bool CanUpdateProperty(ModelMetadata propertyMetadata) { return CanUpdatePropertyInternal(propertyMetadata); } internal static bool CanUpdatePropertyInternal(ModelMetadata propertyMetadata) { return (!propertyMetadata.IsReadOnly || CanUpdateReadOnlyProperty(propertyMetadata.ModelType)); } private static bool CanUpdateReadOnlyProperty(Type propertyType) { // Value types have copy-by-value semantics, which prevents us from updating // properties that are marked readonly. if (propertyType.IsValueType) { return false; } // Arrays are strange beasts since their contents are mutable but their sizes aren't. // Therefore we shouldn't even try to update these. Further reading: // http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx if (propertyType.IsArray) { return false; } // Special-case known immutable reference types if (propertyType == typeof(string)) { return false; } return true; } private ComplexModelDto CreateAndPopulateDto(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, IEnumerable propertyMetadatas) { // create a DTO and call into the DTO binder ComplexModelDto originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas); ExtensibleModelBindingContext dtoBindingContext = new ExtensibleModelBindingContext(bindingContext) { ModelMetadata = MetadataProvider.GetMetadataForType(() => originalDto, typeof(ComplexModelDto)), ModelName = bindingContext.ModelName }; IExtensibleModelBinder dtoBinder = bindingContext.ModelBinderProviders.GetRequiredBinder(controllerContext, dtoBindingContext); dtoBinder.BindModel(controllerContext, dtoBindingContext); return (ComplexModelDto)dtoBindingContext.Model; } protected virtual object CreateModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { // If the Activator throws an exception, we want to propagate it back up the call stack, since the application // developer should know that this was an invalid type to try to bind to. try { return Activator.CreateInstance(bindingContext.ModelType); } catch (MissingMethodException exception) { // Ensure thrown exception contains the type name. Might be down a few levels. MissingMethodException replacementException = ModelBinderUtil.EnsureDebuggableException(exception, bindingContext.ModelType.FullName); if (replacementException != null) { throw replacementException; } throw; } } // Called when the property setter null check failed, allows us to add our own error message to ModelState. internal static EventHandler CreateNullCheckFailedHandler(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue) { return (sender, e) => { ModelValidationNode validationNode = (ModelValidationNode)sender; ModelStateDictionary modelState = e.ControllerContext.Controller.ViewData.ModelState; if (modelState.IsValidField(validationNode.ModelStateKey)) { string errorMessage = ModelBinderConfig.ValueRequiredErrorMessageProvider(controllerContext, modelMetadata, incomingValue); if (errorMessage != null) { modelState.AddModelError(validationNode.ModelStateKey, errorMessage); } } }; } protected virtual void EnsureModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { if (bindingContext.Model == null) { bindingContext.ModelMetadata.Model = CreateModel(controllerContext, bindingContext); } } protected virtual IEnumerable GetMetadataForProperties(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { // keep a set of the required properties so that we can cross-reference bound properties later HashSet requiredProperties; HashSet skipProperties; GetRequiredPropertiesCollection(bindingContext.ModelType, out requiredProperties, out skipProperties); return from propertyMetadata in bindingContext.ModelMetadata.Properties let propertyName = propertyMetadata.PropertyName let shouldUpdateProperty = requiredProperties.Contains(propertyName) || !skipProperties.Contains(propertyName) where shouldUpdateProperty && CanUpdateProperty(propertyMetadata) select propertyMetadata; } private static object GetPropertyDefaultValue(PropertyDescriptor propertyDescriptor) { DefaultValueAttribute attr = propertyDescriptor.Attributes.OfType().FirstOrDefault(); return (attr != null) ? attr.Value : null; } internal static void GetRequiredPropertiesCollection(Type modelType, out HashSet requiredProperties, out HashSet skipProperties) { requiredProperties = new HashSet(StringComparer.OrdinalIgnoreCase); skipProperties = new HashSet(StringComparer.OrdinalIgnoreCase); // Use attributes on the property before attributes on the type. ICustomTypeDescriptor modelDescriptor = TypeDescriptorHelper.Get(modelType); PropertyDescriptorCollection propertyDescriptors = modelDescriptor.GetProperties(); BindingBehaviorAttribute typeAttr = modelDescriptor.GetAttributes().OfType().SingleOrDefault(); foreach (PropertyDescriptor propertyDescriptor in propertyDescriptors) { BindingBehaviorAttribute propAttr = propertyDescriptor.Attributes.OfType().SingleOrDefault(); BindingBehaviorAttribute workingAttr = propAttr ?? typeAttr; if (workingAttr != null) { switch (workingAttr.Behavior) { case BindingBehavior.Required: requiredProperties.Add(propertyDescriptor.Name); break; case BindingBehavior.Never: skipProperties.Add(propertyDescriptor.Name); break; } } } } internal void ProcessDto(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, ComplexModelDto dto) { HashSet requiredProperties; HashSet skipProperties; GetRequiredPropertiesCollection(bindingContext.ModelType, out requiredProperties, out skipProperties); // Are all of the required fields accounted for? HashSet missingRequiredProperties = new HashSet(requiredProperties); missingRequiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName)); string missingPropertyName = missingRequiredProperties.FirstOrDefault(); if (missingPropertyName != null) { string fullPropertyKey = ModelBinderUtil.CreatePropertyModelName(bindingContext.ModelName, missingPropertyName); throw Error.BindingBehavior_ValueNotFound(fullPropertyKey); } // for each property that was bound, call the setter, recording exceptions as necessary foreach (var entry in dto.Results) { ModelMetadata propertyMetadata = entry.Key; ComplexModelDtoResult dtoResult = entry.Value; if (dtoResult != null) { SetProperty(controllerContext, bindingContext, propertyMetadata, dtoResult); bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode); } } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We're recording this exception so that we can act on it later.")] protected virtual void SetProperty(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, ModelMetadata propertyMetadata, ComplexModelDtoResult dtoResult) { PropertyDescriptor propertyDescriptor = TypeDescriptorHelper.Get(bindingContext.ModelType).GetProperties().Find(propertyMetadata.PropertyName, true /* ignoreCase */); if (propertyDescriptor == null || propertyDescriptor.IsReadOnly) { return; // nothing to do } object value = dtoResult.Model ?? GetPropertyDefaultValue(propertyDescriptor); propertyMetadata.Model = value; // 'Required' validators need to run first so that we can provide useful error messages if // the property setters throw, e.g. if we're setting entity keys to null. See comments in // DefaultModelBinder.SetProperty() for more information. if (value == null) { string modelStateKey = dtoResult.ValidationNode.ModelStateKey; if (bindingContext.ModelState.IsValidField(modelStateKey)) { ModelValidator requiredValidator = ModelValidatorProviders.Providers.GetValidators(propertyMetadata, controllerContext).Where(v => v.IsRequired).FirstOrDefault(); if (requiredValidator != null) { foreach (ModelValidationResult validationResult in requiredValidator.Validate(bindingContext.Model)) { bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message); } } } } if (value != null || TypeHelpers.TypeAllowsNullValue(propertyDescriptor.PropertyType)) { try { propertyDescriptor.SetValue(bindingContext.Model, value); } catch (Exception ex) { // don't display a duplicate error message if a binding error has already occurred for this field string modelStateKey = dtoResult.ValidationNode.ModelStateKey; if (bindingContext.ModelState.IsValidField(modelStateKey)) { bindingContext.ModelState.AddModelError(modelStateKey, ex); } } } else { // trying to set a non-nullable value type to null, need to make sure there's a message string modelStateKey = dtoResult.ValidationNode.ModelStateKey; if (bindingContext.ModelState.IsValidField(modelStateKey)) { dtoResult.ValidationNode.Validated += CreateNullCheckFailedHandler(controllerContext, propertyMetadata, value); } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/MutableObjectModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class MutableObjectModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { // no values to bind return null; } if (bindingContext.ModelType == typeof(ComplexModelDto)) { // forbidden type - will cause a stack overflow if we try binding this type return null; } return new MutableObjectModelBinder(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/SimpleModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Returns a user-specified binder for a given type. public sealed class SimpleModelBinderProvider : ModelBinderProvider { private readonly Func _modelBinderFactory; private readonly Type _modelType; public SimpleModelBinderProvider(Type modelType, IExtensibleModelBinder modelBinder) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinder == null) { throw new ArgumentNullException("modelBinder"); } _modelType = modelType; _modelBinderFactory = () => modelBinder; } public SimpleModelBinderProvider(Type modelType, Func modelBinderFactory) { if (modelType == null) { throw new ArgumentNullException("modelType"); } if (modelBinderFactory == null) { throw new ArgumentNullException("modelBinderFactory"); } _modelType = modelType; _modelBinderFactory = modelBinderFactory; } public Type ModelType { get { return _modelType; } } public bool SuppressPrefixCheck { get; set; } public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); if (bindingContext.ModelType == ModelType) { if (SuppressPrefixCheck || bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { return _modelBinderFactory(); } } return null; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/TypeConverterModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class TypeConverterModelBinder : IExtensibleModelBinder { [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded to be acted upon later.")] [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.Web.Mvc.ValueProviderResult.ConvertTo(System.Type)", Justification = "The ValueProviderResult already has the necessary context to perform a culture-aware conversion.")] public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == null) { return false; // no entry } object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); } catch (Exception ex) { if (IsFormatException(ex)) { // there was a type conversion failure string errorString = ModelBinderConfig.TypeConversionErrorMessageProvider(controllerContext, bindingContext.ModelMetadata, valueProviderResult.AttemptedValue); if (errorString != null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorString); } } else { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); } return false; } ModelBinderUtil.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); bindingContext.Model = newModel; return true; } private static bool IsFormatException(Exception ex) { for (; ex != null; ex = ex.InnerException) { if (ex is FormatException) { return true; } } return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/TypeConverterModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Returns a binder that can perform conversions using a .NET TypeConverter. public sealed class TypeConverterModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == null) { return null; // no value to convert } if (!TypeDescriptor.GetConverter(bindingContext.ModelType).CanConvertFrom(typeof(string))) { return null; // this type cannot be converted } return new TypeConverterModelBinder(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/TypeMatchModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { public sealed class TypeMatchModelBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { ValueProviderResult valueProviderResult = GetCompatibleValueProviderResult(bindingContext); if (valueProviderResult == null) { return false; // conversion would have failed } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); object model = valueProviderResult.RawValue; ModelBinderUtil.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model); bindingContext.Model = model; return true; } internal static ValueProviderResult GetCompatibleValueProviderResult(ExtensibleModelBindingContext bindingContext) { ModelBinderUtil.ValidateBindingContext(bindingContext); ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == null) { return null; // the value doesn't exist } if (!TypeHelpers.IsCompatibleObject(bindingContext.ModelType, valueProviderResult.RawValue)) { return null; // value is of incompatible type } return valueProviderResult; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelBinding/TypeMatchModelBinderProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; namespace Microsoft.Web.Mvc.ModelBinding { // Returns a binder that can extract a ValueProviderResult.RawValue and return it directly. [ModelBinderProviderOptions(FrontOfList = true)] public sealed class TypeMatchModelBinderProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return (TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext) != null) ? new TypeMatchModelBinder() : null /* no match */; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ModelCopier.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; namespace Microsoft.Web.Mvc { public static class ModelCopier { public static void CopyCollection(IEnumerable from, ICollection to) { if (from == null || to == null || to.IsReadOnly) { return; } to.Clear(); foreach (T element in from) { to.Add(element); } } public static void CopyModel(object from, object to) { if (from == null || to == null) { return; } PropertyDescriptorCollection fromProperties = TypeDescriptor.GetProperties(from); PropertyDescriptorCollection toProperties = TypeDescriptor.GetProperties(to); foreach (PropertyDescriptor fromProperty in fromProperties) { PropertyDescriptor toProperty = toProperties.Find(fromProperty.Name, true /* ignoreCase */); if (toProperty != null && !toProperty.IsReadOnly) { // Can from.Property reference just be assigned directly to to.Property reference? bool isDirectlyAssignable = toProperty.PropertyType.IsAssignableFrom(fromProperty.PropertyType); // Is from.Property just the nullable form of to.Property? bool liftedValueType = (isDirectlyAssignable) ? false : (Nullable.GetUnderlyingType(fromProperty.PropertyType) == toProperty.PropertyType); if (isDirectlyAssignable || liftedValueType) { object fromValue = fromProperty.GetValue(from); if (isDirectlyAssignable || (fromValue != null && liftedValueType)) { toProperty.SetValue(to, fromValue); } } } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/MvcSerializer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Runtime.Serialization; using System.Web.Security; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public class MvcSerializer { private static readonly string[] _machineKeyPurposes = new string[] { "Microsoft.Web.Mvc.MvcSerializer.v1" }; private static SerializationException CreateSerializationException(Exception innerException) { return new SerializationException(MvcResources.MvcSerializer_DeserializationFailed, innerException); } public virtual object Deserialize(string serializedValue) { return Deserialize(serializedValue, MachineKeyWrapper.Instance); } internal static object Deserialize(string serializedValue, IMachineKey machineKey) { if (String.IsNullOrEmpty(serializedValue)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "serializedValue"); } try { // First, need to decrypt / verify data byte[] rawBytes = machineKey.Unprotect(serializedValue, _machineKeyPurposes); // Finally, deserialize the object graph using (MemoryStream ms = new MemoryStream(rawBytes, 0, rawBytes.Length)) { return DeserializeGraph(ms); } } catch (Exception ex) { throw CreateSerializationException(ex); } } // Deserializes a stream to a graph using the NetDataContractSerializer (binary mode) private static object DeserializeGraph(Stream rawBytes) { using (XmlDictionaryReader dr = XmlDictionaryReader.CreateBinaryReader(rawBytes, XmlDictionaryReaderQuotas.Max)) { object deserialized = new NetDataContractSerializer().ReadObject(dr); return deserialized; } } public virtual string Serialize(object state) { return Serialize(state, MachineKeyWrapper.Instance); } internal static string Serialize(object state, IMachineKey machineKey) { try { // First, need to serialize the object graph byte[] rawBytes; using (MemoryStream ms = new MemoryStream()) { SerializeGraph(ms, state); rawBytes = ms.ToArray(); } // Then, encrypt / sign data return machineKey.Protect(rawBytes, _machineKeyPurposes); } catch (Exception ex) { throw CreateSerializationException(ex); } } // Serializes a graph to a byte array using the NetDataContractSerializer (binary mode) private static void SerializeGraph(Stream outputStream, object graph) { using (XmlDictionaryWriter dw = XmlDictionaryWriter.CreateBinaryWriter(outputStream, null, null, ownsStream: false)) { new NetDataContractSerializer().WriteObject(dw, graph); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("Microsoft.Web.Mvc.dll")] [assembly: AssemblyDescription("Microsoft.Web.Mvc.dll")] [assembly: Guid("f3507a98-9429-404b-9e0e-1b426a5b3ad5")] [assembly: InternalsVisibleTo("Microsoft.Web.Mvc.Test")] ================================================ FILE: src/Microsoft.Web.Mvc/Properties/MvcResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.18051 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.Web.Mvc.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class MvcResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal MvcResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Web.Mvc.Properties.MvcResources", typeof(MvcResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to The {0} field only accepts files with one of the following content types: {1}.. /// internal static string AcceptAttribute_Invalid { get { return ResourceManager.GetString("AcceptAttribute_Invalid", resourceCulture); } } /// /// Looks up a localized string similar to A value for '{0}' is required but was not present in the request.. /// internal static string BindingBehavior_ValueNotFound { get { return ResourceManager.GetString("BindingBehavior_ValueNotFound", resourceCulture); } } /// /// Looks up a localized string similar to Value cannot be null or empty.. /// internal static string Common_NullOrEmpty { get { return ResourceManager.GetString("Common_NullOrEmpty", resourceCulture); } } /// /// Looks up a localized string similar to The type '{0}' does not implement the interface '{1}'.. /// internal static string Common_TypeMustImplementInterface { get { return ResourceManager.GetString("Common_TypeMustImplementInterface", resourceCulture); } } /// /// Looks up a localized string similar to The 'Name' property must be set.. /// internal static string CommonControls_NameRequired { get { return ResourceManager.GetString("CommonControls_NameRequired", resourceCulture); } } /// /// Looks up a localized string similar to The {0} field is not a valid credit card number.. /// internal static string CreditCardAttribute_Invalid { get { return ResourceManager.GetString("CreditCardAttribute_Invalid", resourceCulture); } } /// /// Looks up a localized string similar to Sample Item. /// internal static string DropDownList_SampleItem { get { return ResourceManager.GetString("DropDownList_SampleItem", resourceCulture); } } /// /// Looks up a localized string similar to DynamicViewDataDictionary only supports single indexers.. /// internal static string DynamicViewDataDictionary_SingleIndexerOnly { get { return ResourceManager.GetString("DynamicViewDataDictionary_SingleIndexerOnly", resourceCulture); } } /// /// Looks up a localized string similar to DynamicViewDataDictionary only supports string-based indexers.. /// internal static string DynamicViewDataDictionary_StringIndexerOnly { get { return ResourceManager.GetString("DynamicViewDataDictionary_StringIndexerOnly", resourceCulture); } } /// /// Looks up a localized string similar to The property {0} doesn't exist. There are no public properties on this object.. /// internal static string DynamicViewPage_NoProperties { get { return ResourceManager.GetString("DynamicViewPage_NoProperties", resourceCulture); } } /// /// Looks up a localized string similar to The property {0} doesn't exist. Supported properties are: {1}.. /// internal static string DynamicViewPage_PropertyDoesNotExist { get { return ResourceManager.GetString("DynamicViewPage_PropertyDoesNotExist", resourceCulture); } } /// /// Looks up a localized string similar to The {0} field is not a valid e-mail address.. /// internal static string EmailAddressAttribute_Invalid { get { return ResourceManager.GetString("EmailAddressAttribute_Invalid", resourceCulture); } } /// /// Looks up a localized string similar to The method '{0}' is an asynchronous completion method and cannot be called directly.. /// internal static string ExpressionHelper_CannotCallCompletedMethod { get { return ResourceManager.GetString("ExpressionHelper_CannotCallCompletedMethod", resourceCulture); } } /// /// Looks up a localized string similar to The method '{0}' is marked [NonAction] and cannot be called directly.. /// internal static string ExpressionHelper_CannotCallNonAction { get { return ResourceManager.GetString("ExpressionHelper_CannotCallNonAction", resourceCulture); } } /// /// Looks up a localized string similar to Cannot route to class named 'Controller'.. /// internal static string ExpressionHelper_CannotRouteToController { get { return ResourceManager.GetString("ExpressionHelper_CannotRouteToController", resourceCulture); } } /// /// Looks up a localized string similar to Expression must be a method call.. /// internal static string ExpressionHelper_MustBeMethodCall { get { return ResourceManager.GetString("ExpressionHelper_MustBeMethodCall", resourceCulture); } } /// /// Looks up a localized string similar to Controller name must end in 'Controller'.. /// internal static string ExpressionHelper_TargetMustEndInController { get { return ResourceManager.GetString("ExpressionHelper_TargetMustEndInController", resourceCulture); } } /// /// Looks up a localized string similar to The new model binding system cannot be used when a property whitelist or blacklist has been specified in [Bind] or via the call to UpdateModel() / TryUpdateModel(). Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead.. /// internal static string ExtensibleModelBinderAdapter_PropertyFilterMustNotBeSet { get { return ResourceManager.GetString("ExtensibleModelBinderAdapter_PropertyFilterMustNotBeSet", resourceCulture); } } /// /// Looks up a localized string similar to The {0} field only accepts files with the following extensions: {1}. /// internal static string FileExtensionsAttribute_Invalid { get { return ResourceManager.GetString("FileExtensionsAttribute_Invalid", resourceCulture); } } /// /// Looks up a localized string similar to The type '{0}' is not an open generic type.. /// internal static string GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType { get { return ResourceManager.GetString("GenericModelBinderProvider_ParameterMustSpecifyOpenGenericType", resourceCulture); } } /// /// Looks up a localized string similar to The open model type '{0}' has {1} generic type argument(s), but the open binder type '{2}' has {3} generic type argument(s). The binder type must not be an open generic type or must have the same number of generic arguments as the open model type.. /// internal static string GenericModelBinderProvider_TypeArgumentCountMismatch { get { return ResourceManager.GetString("GenericModelBinderProvider_TypeArgumentCountMismatch", resourceCulture); } } /// /// Looks up a localized string similar to There is no ViewData item with the key '{0}' of type '{1}'.. /// internal static string HtmlHelper_MissingSelectData { get { return ResourceManager.GetString("HtmlHelper_MissingSelectData", resourceCulture); } } /// /// Looks up a localized string similar to The ViewData item with the key '{0}' is of type '{1}' but needs to be of type '{2}'.. /// internal static string HtmlHelper_WrongSelectDataType { get { return ResourceManager.GetString("HtmlHelper_WrongSelectDataType", resourceCulture); } } /// /// Looks up a localized string similar to The value '{0}' is not valid for {1}.. /// internal static string ModelBinderConfig_ValueInvalid { get { return ResourceManager.GetString("ModelBinderConfig_ValueInvalid", resourceCulture); } } /// /// Looks up a localized string similar to A value is required.. /// internal static string ModelBinderConfig_ValueRequired { get { return ResourceManager.GetString("ModelBinderConfig_ValueRequired", resourceCulture); } } /// /// Looks up a localized string similar to A binder for type {0} could not be located.. /// internal static string ModelBinderProviderCollection_BinderForTypeNotFound { get { return ResourceManager.GetString("ModelBinderProviderCollection_BinderForTypeNotFound", resourceCulture); } } /// /// Looks up a localized string similar to The type '{0}' does not subclass {1} or implement the interface {2}.. /// internal static string ModelBinderProviderCollection_InvalidBinderType { get { return ResourceManager.GetString("ModelBinderProviderCollection_InvalidBinderType", resourceCulture); } } /// /// Looks up a localized string similar to The model of type '{0}' has a [Bind] attribute. The new model binding system cannot be used with models that have type-level [Bind] attributes. Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead.. /// internal static string ModelBinderProviderCollection_TypeCannotHaveBindAttribute { get { return ResourceManager.GetString("ModelBinderProviderCollection_TypeCannotHaveBindAttribute", resourceCulture); } } /// /// Looks up a localized string similar to {0} Object type '{1}'.. /// internal static string ModelBinderUtil_CannotCreateInstance { get { return ResourceManager.GetString("ModelBinderUtil_CannotCreateInstance", resourceCulture); } } /// /// Looks up a localized string similar to The binding context has a null Model, but this binder requires a non-null model of type '{0}'.. /// internal static string ModelBinderUtil_ModelCannotBeNull { get { return ResourceManager.GetString("ModelBinderUtil_ModelCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to The binding context has a Model of type '{0}', but this binder can only operate on models of type '{1}'.. /// internal static string ModelBinderUtil_ModelInstanceIsWrong { get { return ResourceManager.GetString("ModelBinderUtil_ModelInstanceIsWrong", resourceCulture); } } /// /// Looks up a localized string similar to The binding context cannot have a null ModelMetadata.. /// internal static string ModelBinderUtil_ModelMetadataCannotBeNull { get { return ResourceManager.GetString("ModelBinderUtil_ModelMetadataCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to The binding context has a ModelType of '{0}', but this binder can only operate on models of type '{1}'.. /// internal static string ModelBinderUtil_ModelTypeIsWrong { get { return ResourceManager.GetString("ModelBinderUtil_ModelTypeIsWrong", resourceCulture); } } /// /// Looks up a localized string similar to The ModelMetadata property must be set before accessing this property.. /// internal static string ModelBindingContext_ModelMetadataMustBeSet { get { return ResourceManager.GetString("ModelBindingContext_ModelMetadataMustBeSet", resourceCulture); } } /// /// Looks up a localized string similar to Deserialization failed. Verify that the data is being deserialized using the same SerializationMode with which it was serialized. Otherwise see the inner exception.. /// internal static string MvcSerializer_DeserializationFailed { get { return ResourceManager.GetString("MvcSerializer_DeserializationFailed", resourceCulture); } } /// /// Looks up a localized string similar to Error dispatching on controller {0}, conflicting actions matched: {1}.. /// internal static string ResourceControllerFactory_ConflictingActions { get { return ResourceManager.GetString("ResourceControllerFactory_ConflictingActions", resourceCulture); } } /// /// Looks up a localized string similar to Error dispatching on controller {0}, no actions matched.. /// internal static string ResourceControllerFactory_NoActions { get { return ResourceManager.GetString("ResourceControllerFactory_NoActions", resourceCulture); } } /// /// Looks up a localized string similar to Format '{0}' is not supported.. /// internal static string Resources_UnsupportedFormat { get { return ResourceManager.GetString("Resources_UnsupportedFormat", resourceCulture); } } /// /// Looks up a localized string similar to Unsupported Media Type: '{0}'.. /// internal static string Resources_UnsupportedMediaType { get { return ResourceManager.GetString("Resources_UnsupportedMediaType", resourceCulture); } } /// /// Looks up a localized string similar to The {0} field is not a valid fully-qualified http, https, or ftp URL.. /// internal static string UrlAttribute_Invalid { get { return ResourceManager.GetString("UrlAttribute_Invalid", resourceCulture); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Properties/MvcResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Value cannot be null or empty. Cannot route to class named 'Controller'. Expression must be a method call. Controller name must end in 'Controller'. There is no ViewData item with the key '{0}' of type '{1}'. The ViewData item with the key '{0}' is of type '{1}' but needs to be of type '{2}'. The 'Name' property must be set. Deserialization failed. Verify that the data is being deserialized using the same SerializationMode with which it was serialized. Otherwise see the inner exception. Unsupported Media Type: '{0}'. Format '{0}' is not supported. The method '{0}' is an asynchronous completion method and cannot be called directly. The method '{0}' is marked [NonAction] and cannot be called directly. The binding context has a null Model, but this binder requires a non-null model of type '{0}'. The binding context has a Model of type '{0}', but this binder can only operate on models of type '{1}'. The binding context cannot have a null ModelMetadata. The binding context has a ModelType of '{0}', but this binder can only operate on models of type '{1}'. The value '{0}' is not valid for {1}. A value is required. A binder for type {0} could not be located. The ModelMetadata property must be set before accessing this property. The type '{0}' does not implement the interface '{1}'. The type '{0}' is not an open generic type. The open model type '{0}' has {1} generic type argument(s), but the open binder type '{2}' has {3} generic type argument(s). The binder type must not be an open generic type or must have the same number of generic arguments as the open model type. A value for '{0}' is required but was not present in the request. The new model binding system cannot be used when a property whitelist or blacklist has been specified in [Bind] or via the call to UpdateModel() / TryUpdateModel(). Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead. The model of type '{0}' has a [Bind] attribute. The new model binding system cannot be used with models that have type-level [Bind] attributes. Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead. The type '{0}' does not subclass {1} or implement the interface {2}. DynamicViewDataDictionary only supports single indexers. DynamicViewDataDictionary only supports string-based indexers. The property {0} doesn't exist. There are no public properties on this object. The property {0} doesn't exist. Supported properties are: {1}. Sample Item Error dispatching on controller {0}, conflicting actions matched: {1}. Error dispatching on controller {0}, no actions matched. The {0} field only accepts files with the following extensions: {1} The {0} field is not a valid credit card number. The {0} field is not a valid e-mail address. The {0} field is not a valid fully-qualified http, https, or ftp URL. The {0} field only accepts files with one of the following content types: {1}. {0} Object type '{1}'. ================================================ FILE: src/Microsoft.Web.Mvc/RadioExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web.Mvc; using System.Web.Mvc.Html; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public static class RadioListExtensions { public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name) { return RadioButtonList(htmlHelper, name, (IDictionary)null); } public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name, object htmlAttributes) { return RadioButtonList(htmlHelper, name, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name, IDictionary htmlAttributes) { IEnumerable selectList = htmlHelper.GetSelectData(name); return htmlHelper.RadioButtonListInternal(name, selectList, true /* usedViewData */, htmlAttributes); } public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name, IEnumerable selectList) { return RadioButtonList(htmlHelper, name, selectList, null); } public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name, IEnumerable selectList, object htmlAttributes) { return RadioButtonList(htmlHelper, name, selectList, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); } public static MvcHtmlString[] RadioButtonList(this HtmlHelper htmlHelper, string name, IEnumerable selectList, IDictionary htmlAttributes) { return htmlHelper.RadioButtonListInternal(name, selectList, false /* usedViewData */, htmlAttributes); } private static IEnumerable GetSelectData(this HtmlHelper htmlHelper, string name) { object o = null; if (htmlHelper.ViewData != null) { o = htmlHelper.ViewData.Eval(name); } if (o == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.HtmlHelper_MissingSelectData, name, typeof(IEnumerable))); } IEnumerable selectList = o as IEnumerable; if (selectList == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.HtmlHelper_WrongSelectDataType, name, o.GetType().FullName, typeof(IEnumerable))); } return selectList; } private static MvcHtmlString[] RadioButtonListInternal(this HtmlHelper htmlHelper, string name, IEnumerable selectList, bool usedViewData, IDictionary htmlAttributes) { if (String.IsNullOrEmpty(name)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name"); } if (selectList == null) { throw new ArgumentNullException("selectList"); } // If we haven't already used ViewData to get the entire list of items then we need to // use the ViewData-supplied value before using the parameter-supplied value. if (!usedViewData) { object defaultValue = htmlHelper.ViewData.Eval(name); if (defaultValue != null) { IEnumerable defaultValues = new[] { defaultValue }; IEnumerable values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); HashSet selectedValues = new HashSet(values, StringComparer.OrdinalIgnoreCase); List newSelectList = new List(); foreach (SelectListItem item in selectList) { item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text); newSelectList.Add(item); } selectList = newSelectList; } } IEnumerable radioButtons = selectList.Select(item => htmlHelper.RadioButton(name, item.Value, item.Selected, htmlAttributes)); return radioButtons.ToArray(); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ReaderWriterCache.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; namespace Microsoft.Web.Mvc { [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Instances of this type are meant to be singletons.")] internal abstract class ReaderWriterCache { private readonly Dictionary _cache; private readonly ReaderWriterLockSlim _readerWriterLock = new ReaderWriterLockSlim(); protected ReaderWriterCache() : this(null) { } protected ReaderWriterCache(IEqualityComparer comparer) { _cache = new Dictionary(comparer); } protected Dictionary Cache { get { return _cache; } } protected TValue FetchOrCreateItem(TKey key, Func creator) { // first, see if the item already exists in the cache _readerWriterLock.EnterReadLock(); try { TValue existingEntry; if (_cache.TryGetValue(key, out existingEntry)) { return existingEntry; } } finally { _readerWriterLock.ExitReadLock(); } // insert the new item into the cache TValue newEntry = creator(); _readerWriterLock.EnterWriteLock(); try { TValue existingEntry; if (_cache.TryGetValue(key, out existingEntry)) { // another thread already inserted an item, so use that one return existingEntry; } _cache[key] = newEntry; return newEntry; } finally { _readerWriterLock.ExitWriteLock(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/ActionType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Resources { /// /// This enum is used by the UrlHelper extension methods to create links within resource controllers /// public enum ActionType { Create, GetCreateForm, Index, Retrieve, Update, GetUpdateForm, Delete, } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/AjaxHelperExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using System.Web.Mvc.Ajax; using System.Web.Mvc.Html; namespace Microsoft.Web.Mvc.Resources { public static class AjaxHelperExtensions { /// /// Generates the Form preamble, defaulting the link for the Retrieve action /// /// /// /// /// /// public static MvcForm BeginResourceForm(this AjaxHelper ajax, string controllerName, object routeValues, AjaxOptions ajaxOptions) { return ajax.BeginResourceForm(controllerName, routeValues, ajaxOptions, ActionType.Retrieve); } /// /// Generates the Form preamble /// /// /// /// /// /// /// public static MvcForm BeginResourceForm(this AjaxHelper ajax, string controllerName, object routeValues, AjaxOptions ajaxOptions, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return ajax.BeginRouteForm(controllerName + "-editForm", routeValues, ajaxOptions); case ActionType.GetCreateForm: return ajax.BeginRouteForm(controllerName + "-createForm", ajaxOptions); case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: // can we use ajaxOptions to either add the header? MvcForm form = ajax.BeginRouteForm(controllerName, routeValues, ajaxOptions); return form; case ActionType.Create: return ajax.BeginRouteForm(controllerName + "-create", ajaxOptions); case ActionType.Index: return ajax.BeginRouteForm(controllerName + "-index", ajaxOptions); default: throw new ArgumentOutOfRangeException("actionType"); } } /// /// Generates a link to the resource controller, defaulting to the Retrieve action /// /// /// /// /// /// public static MvcHtmlString ResourceLink(this AjaxHelper ajax, string controllerName, object routeValues, AjaxOptions ajaxOptions) { return ajax.ResourceLink(controllerName, controllerName, routeValues, ajaxOptions, ActionType.Retrieve); } /// /// Generates a link to the resource controller, defaulting to the Retrieve action /// /// /// /// /// /// /// public static MvcHtmlString ResourceLink(this AjaxHelper ajax, string controllerName, string linkText, object routeValues, AjaxOptions ajaxOptions) { return ajax.ResourceLink(linkText, controllerName, routeValues, ajaxOptions, ActionType.Retrieve); } /// /// Generates a link to the resource controller /// /// /// /// /// /// /// /// public static MvcHtmlString ResourceLink(this AjaxHelper ajax, string controllerName, string linkText, object routeValues, AjaxOptions ajaxOptions, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return ajax.RouteLink(linkText, controllerName + "-editForm", routeValues, ajaxOptions); case ActionType.GetCreateForm: return ajax.RouteLink(linkText, controllerName + "-createForm", ajaxOptions); case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: return ajax.RouteLink(linkText, controllerName, routeValues, ajaxOptions); case ActionType.Create: return ajax.RouteLink(linkText, controllerName + "-create", ajaxOptions); case ActionType.Index: return ajax.RouteLink(linkText, controllerName + "-index", ajaxOptions); default: throw new ArgumentOutOfRangeException("actionType"); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/AtomEntryActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Net; using System.Net.Mime; using System.ServiceModel.Syndication; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// An ActionResult that can render a SyndicationItem to the Atom 1.0 entry format /// public class AtomEntryActionResult : ActionResult { private ContentType contentType; private SyndicationItem item; /// /// The content type defaults to application/atom+xml; type=entry /// /// public AtomEntryActionResult(SyndicationItem item) : this(item, new ContentType("application/atom+xml;type=entry")) { } public AtomEntryActionResult(SyndicationItem item, ContentType contentType) { if (item == null) { throw new ArgumentNullException("item"); } if (contentType == null) { throw new ArgumentNullException("contentType"); } this.item = item; this.contentType = contentType; } public ContentType ContentType { get { return this.contentType; } } public SyndicationItem Item { get { return this.item; } } public override void ExecuteResult(ControllerContext context) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(this.ContentType.CharSet)) { try { encoding = Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this.ContentType)); } } XmlWriterSettings settings = new XmlWriterSettings { Encoding = encoding }; this.ContentType.CharSet = settings.Encoding.HeaderName; context.HttpContext.Response.ContentType = this.ContentType.ToString(); using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.OutputStream, settings)) { this.Item.GetAtom10Formatter().WriteTo(writer); writer.Flush(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/AtomFeedActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Net; using System.Net.Mime; using System.ServiceModel.Syndication; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// An ActionResult that can render a SyndicationFeed to the Atom 1.0 feed format /// public class AtomFeedActionResult : ActionResult { private ContentType contentType; private SyndicationFeed feed; /// /// The content type defaults to application/atom+xml /// /// public AtomFeedActionResult(SyndicationFeed feed) : this(feed, new ContentType("application/atom+xml")) { } public AtomFeedActionResult(SyndicationFeed feed, ContentType contentType) { if (feed == null) { throw new ArgumentNullException("feed"); } if (contentType == null) { throw new ArgumentNullException("contentType"); } this.feed = feed; this.contentType = contentType; } public ContentType ContentType { get { return this.contentType; } } public SyndicationFeed Feed { get { return this.feed; } } public override void ExecuteResult(ControllerContext context) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(this.ContentType.CharSet)) { try { encoding = Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this.ContentType)); } } XmlWriterSettings settings = new XmlWriterSettings { Encoding = encoding }; this.ContentType.CharSet = settings.Encoding.HeaderName; context.HttpContext.Response.ContentType = this.ContentType.ToString(); using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.OutputStream, settings)) { this.Feed.GetAtom10Formatter().WriteTo(writer); writer.Flush(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/AtomServiceDocumentActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Net; using System.Net.Mime; using System.ServiceModel.Syndication; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// An ActionResult that can render a ServiceDocument to the Atom 1.0 ServiceDocument format /// public class AtomServiceDocumentActionResult : ActionResult { private ContentType contentType; private ServiceDocument document; /// /// The content type defaults to application/atomsvc+xml /// /// public AtomServiceDocumentActionResult(ServiceDocument document) : this(document, new ContentType("application/atomsvc+xml")) { } public AtomServiceDocumentActionResult(ServiceDocument document, ContentType contentType) { if (document == null) { throw new ArgumentNullException("document"); } if (contentType == null) { throw new ArgumentNullException("contentType"); } this.document = document; this.contentType = contentType; } public ContentType ContentType { get { return this.contentType; } } public ServiceDocument Document { get { return this.document; } } public override void ExecuteResult(ControllerContext context) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(this.ContentType.CharSet)) { try { encoding = Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this.ContentType)); } } XmlWriterSettings settings = new XmlWriterSettings { Encoding = encoding }; this.ContentType.CharSet = settings.Encoding.HeaderName; context.HttpContext.Response.ContentType = this.ContentType.ToString(); using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.OutputStream, settings)) { this.Document.GetFormatter().WriteTo(writer); writer.Flush(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/DataContractJsonActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Net; using System.Net.Mime; using System.Runtime.Serialization.Json; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// An ActionResult that can render an object to the json format using the DataContractJsonSerializer /// public class DataContractJsonActionResult : ActionResult { private ContentType contentType; private object data; /// /// The default content type is application/json /// /// public DataContractJsonActionResult(object data) : this(data, new ContentType("application/json")) { } public DataContractJsonActionResult(object data, ContentType contentType) { this.data = data; this.contentType = contentType; } public ContentType ContentType { get { return this.contentType; } } public object Data { get { return this.data; } } public override void ExecuteResult(ControllerContext context) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(this.ContentType.CharSet)) { try { encoding = Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this.ContentType)); } } DataContractJsonSerializer dcs = new DataContractJsonSerializer(this.Data.GetType()); this.ContentType.CharSet = encoding.HeaderName; context.HttpContext.Response.ContentType = this.ContentType.ToString(); using (XmlWriter writer = JsonReaderWriterFactory.CreateJsonWriter(context.HttpContext.Response.OutputStream, encoding)) { dcs.WriteObject(writer, this.Data); writer.Flush(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/DataContractXmlActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Net; using System.Net.Mime; using System.Runtime.Serialization; using System.Text; using System.Web; using System.Web.Mvc; using System.Xml; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// An ActionResult that can render an object to the xml format using the DataContractSerializer /// public class DataContractXmlActionResult : ActionResult { private ContentType contentType; private object data; /// /// The content type of the response defaults to application/xml /// public DataContractXmlActionResult(object data) : this(data, new ContentType("application/xml")) { } public DataContractXmlActionResult(object data, ContentType contentType) { this.data = data; this.contentType = contentType; } public ContentType ContentType { get { return this.contentType; } } public object Data { get { return this.data; } } public override void ExecuteResult(ControllerContext context) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(this.ContentType.CharSet)) { try { encoding = Encoding.GetEncoding(this.ContentType.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this.ContentType)); } } XmlWriterSettings settings = new XmlWriterSettings { Encoding = encoding }; DataContractSerializer dcs = new DataContractSerializer(this.Data.GetType()); this.ContentType.CharSet = settings.Encoding.HeaderName; context.HttpContext.Response.ContentType = this.ContentType.ToString(); using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.OutputStream, settings)) { dcs.WriteObject(writer, this.Data); writer.Flush(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/DefaultFormatHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using System.Net.Mime; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// Default implementation of FormatHelper /// The results for GetRequestFormat() and GetResponseFormats() are cached on the HttpContext.Items dictionary: /// HttpContext.Items["requestFormat"] /// HttpContext.Items["responseFormat"] /// public class DefaultFormatHelper : FormatHelper { private const string FormatVariableName = "format"; private const string QualityFactor = "q"; public const string RequestFormatKey = "requestFormat"; public const string ResponseFormatKey = "responseFormat"; /// /// Returns the format of a given request, according to the following /// rules: /// 1. If a Content-Type header exists it returns a ContentType for it or fails if one can't be created /// 2. Otherwie, if a Content-Type header does not exists it provides the default ContentType of "application/octet-stream" (per RFC 2616 7.2.1) /// /// The request. /// The format of the request. /// If the format is unrecognized or not supported. public override ContentType GetRequestFormat(RequestContext requestContext) { ContentType result; if (!requestContext.HttpContext.Items.Contains(RequestFormatKey)) { result = GetRequestFormat(requestContext.HttpContext.Request, true); requestContext.HttpContext.Items.Add(RequestFormatKey, result); } else { result = (ContentType)requestContext.HttpContext.Items[RequestFormatKey]; } return result; } internal static ContentType GetRequestFormat(HttpRequestBase request, bool throwOnError) { if (!String.IsNullOrEmpty(request.ContentType)) { ContentType contentType = ParseContentType(request.ContentType); if (contentType != null) { return contentType; } if (throwOnError) { throw new HttpException((int)HttpStatusCode.UnsupportedMediaType, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedMediaType, request.ContentType)); } return null; } return new ContentType(); } /// /// Returns the preferred content type to use for the response, based on the request, according to the following /// rules: /// 1. If the RouteData contains a value for a key called "format", its value is returned as the content type /// 2. Otherwise, if the query string contains a key called "format", its value is returned as the content type /// 3. Otherwise, if the request has an Accepts header, the list of content types in order of preference is returned /// 4. Otherwise, if the request has a content type, its value is returned /// /// The request. /// The formats to use for rendering a response. public override IEnumerable GetResponseFormats(RequestContext requestContext) { IEnumerable result; if (!requestContext.HttpContext.Items.Contains(ResponseFormatKey)) { result = GetResponseFormatsRouteAware(requestContext); requestContext.HttpContext.Items.Add(ResponseFormatKey, result); } else { result = (IEnumerable)requestContext.HttpContext.Items[ResponseFormatKey]; } return result; } private static List GetResponseFormatsRouteAware(RequestContext requestContext) { List result = GetResponseFormatsCore(requestContext.HttpContext.Request); ContentType contentType; if (result == null) { contentType = FormatManager.Current.FormatHelper.GetRequestFormat(requestContext); result = new List(new[] { contentType }); } if (TryGetFromRouteData(requestContext.RouteData, out contentType)) { result.Insert(0, contentType); } return result; } /// /// Returns the preferred content type to use for the response, based on the request, according to the following /// rules: /// 1. If the query string contains a key called "format", its value is returned as the content type /// 2. Otherwise, if the request has an Accepts header, the list of content types in order of preference is returned /// 3. Otherwise, if the request has a content type, its value is returned /// /// /// internal static List GetResponseFormats(HttpRequestBase request) { List result = GetResponseFormatsCore(request); if (result == null) { ContentType contentType = GetRequestFormat(request, true); result = new List(new[] { contentType }); } return result; } private static List GetResponseFormatsCore(HttpRequestBase request) { ContentType contentType; if (TryGetFromUri(request, out contentType)) { return new List(new[] { contentType }); } string[] accepts = request.AcceptTypes; if (accepts != null && accepts.Length > 0) { return GetAcceptHeaderElements(accepts); } return null; } // CONSIDER: we currently don't process the Accept-Charset header, need to take it into account, EG: // Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 private static List GetAcceptHeaderElements(string[] acceptHeaderElements) { List contentTypeList = new List(acceptHeaderElements.Length); foreach (string acceptHeaderElement in acceptHeaderElements) { if (acceptHeaderElement != null) { ContentType contentType = ParseContentType(acceptHeaderElement); // ignore unknown formats to allow fallback if (contentType != null) { contentTypeList.Add(contentType); } } } contentTypeList.Sort(new AcceptHeaderElementComparer()); // CONSIDER: we used the "q" parameter for sorting, so now we strip it // it might be ebtter to strip it later in case someone needs to access it foreach (ContentType ct in contentTypeList) { if (ct.Parameters.ContainsKey(QualityFactor)) { ct.Parameters.Remove(QualityFactor); } } return contentTypeList; } public override bool IsBrowserRequest(RequestContext requestContext) { return IsBrowserRequest(requestContext.HttpContext.Request); } // Parses a string into a ContentType instance, supports // friendly names and enforces a charset (which defaults to utf-8) internal static ContentType ParseContentType(string contentTypeString) { ContentType contentType = null; try { contentType = new ContentType(contentTypeString); } catch (FormatException) { // This may be a friendly name (for example, "xml" instead of "text/xml"). // if so, try mapping to a content type if (!FormatManager.Current.TryMapFormatFriendlyName(contentTypeString, out contentType)) { return null; } } Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(contentType.CharSet)) { try { encoding = Encoding.GetEncoding(contentType.CharSet); } catch (ArgumentException) { return null; } } contentType.CharSet = encoding.HeaderName; return contentType; } // Route-based format override so clients can use a route variable private static bool TryGetFromRouteData(RouteData routeData, out ContentType contentType) { contentType = null; if (routeData != null) { string fromRouteData = routeData.Values[FormatVariableName] as string; if (!String.IsNullOrEmpty(fromRouteData)) { contentType = ParseContentType(fromRouteData); } } return contentType != null; } // Uri-based format override so clients can use a query string // also useful when using the browser where you can't set headerss private static bool TryGetFromUri(HttpRequestBase request, out ContentType contentType) { string fromParams = request.QueryString[FormatVariableName]; if (fromParams != null) { contentType = ParseContentType(fromParams); if (contentType != null) { return true; } } contentType = null; return false; } /// /// Determines whether the specified HTTP request was sent by a Browser. /// A request is considered to be from the browser if: /// it's a GET or POST /// and does not have a non-HTML entity format (XML/JSON) /// and has a known User-Agent header (as determined by the request's BrowserCapabilities property), /// /// The request. /// true if the specified HTTP request is a Browser request; otherwise, false. internal static bool IsBrowserRequest(HttpRequestBase request) { if (!request.IsHttpMethod(HttpVerbs.Get) && !request.IsHttpMethod(HttpVerbs.Post)) { return false; } ContentType requestFormat = GetRequestFormat(request, false); if (requestFormat == null || String.Compare(requestFormat.MediaType, FormatManager.UrlEncoded, StringComparison.OrdinalIgnoreCase) != 0) { if (FormatManager.Current.CanDeserialize(requestFormat)) { return false; } } HttpBrowserCapabilitiesBase browserCapabilities = request.Browser; if (browserCapabilities != null && !String.IsNullOrEmpty(request.Browser.Browser) && request.Browser.Browser != "Unknown") { return true; } return false; } private class AcceptHeaderElementComparer : IComparer { [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "x, y", Justification = "No need to fix this since this is a private class.")] public int Compare(ContentType x, ContentType y) { string[] xTypeSubType = x.MediaType.Split('/'); string[] yTypeSubType = y.MediaType.Split('/'); 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; } private static decimal GetQualityFactor(ContentType contentType) { decimal result; foreach (string key in contentType.Parameters.Keys) { if (String.Equals(QualityFactor, key, StringComparison.OrdinalIgnoreCase)) { if (Decimal.TryParse(contentType.Parameters[key], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out result) && (result <= (decimal)1.0)) { return result; } } } return (decimal)1.0; } private static bool HasParameters(ContentType contentType) { int number = 0; foreach (string param in contentType.Parameters.Keys) { if (!String.Equals(QualityFactor, param, StringComparison.OrdinalIgnoreCase)) { number++; } } return (number > 0); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/DefaultFormatManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.Web.Mvc.Resources { public class DefaultFormatManager : FormatManager { public DefaultFormatManager() { XmlFormatHandler xmlHandler = new XmlFormatHandler(); JsonFormatHandler jsonHandler = new JsonFormatHandler(); this.RequestFormatHandlers.Add(xmlHandler); this.RequestFormatHandlers.Add(jsonHandler); this.ResponseFormatHandlers.Add(xmlHandler); this.ResponseFormatHandlers.Add(jsonHandler); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/FormatHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Mime; using System.Web; using System.Web.Routing; namespace Microsoft.Web.Mvc.Resources { /// /// Base class for content negotiation support /// public abstract class FormatHelper { /// /// Returns the ContentType of a given request. /// /// The request. /// The format of the request. /// If the format is unrecognized or not supported. public abstract ContentType GetRequestFormat(RequestContext requestContext); /// /// Returns a collection of ContentType instances that can be used to render a response to a given request, sorted in priority order. /// /// The request. /// The formats to use for rendering a response. public abstract IEnumerable GetResponseFormats(RequestContext requestContext); /// /// Determines whether the specified HTTP request was sent by a Browser. /// /// The request. /// public abstract bool IsBrowserRequest(RequestContext requestContext); } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/FormatManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Net.Mime; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// Class that maintains a registration of handlers for /// request and response formats /// public class FormatManager { public const string UrlEncoded = "application/x-www-form-urlencoded"; private static FormatManager _current = new DefaultFormatManager(); private Collection _requestHandlers; private Collection _responseHandlers; private FormatHelper _formatHelper; public FormatManager() { this._requestHandlers = new Collection(); this._responseHandlers = new Collection(); this._formatHelper = new DefaultFormatHelper(); } /// /// The list of handlers that can parse the request body /// public Collection RequestFormatHandlers { get { return this._requestHandlers; } } /// /// The list of handlers that can serialize the response body /// public Collection ResponseFormatHandlers { get { return this._responseHandlers; } } public static FormatManager Current { get { return _current; } set { if (value == null) { throw new ArgumentNullException("value"); } _current = value; } } // CONSIDER: the FormatHelper is an abstraction that lets users extend the content negotiation process // we must reconsider the FormatManager/FormatHelper factoring and provide a cleaner way of allowing this same extensibility public FormatHelper FormatHelper { get { return _formatHelper; } set { if (value == null) { throw new ArgumentNullException("value"); } _formatHelper = value; } } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "This is an existing API; this would be a breaking change")] public bool TryDeserialize(ControllerContext controllerContext, ModelBindingContext bindingContext, ContentType requestFormat, out object model) { for (int i = 0; i < this.RequestFormatHandlers.Count; ++i) { if (this.RequestFormatHandlers[i].CanDeserialize(requestFormat)) { model = this.RequestFormatHandlers[i].Deserialize(controllerContext, bindingContext, requestFormat); return true; } } model = null; return false; } public bool CanDeserialize(ContentType contentType) { for (int i = 0; i < this.RequestFormatHandlers.Count; ++i) { if (this.RequestFormatHandlers[i].CanDeserialize(contentType)) { return true; } } return false; } public bool CanSerialize(ContentType responseFormat) { for (int i = 0; i < this.ResponseFormatHandlers.Count; ++i) { if (this.ResponseFormatHandlers[i].CanSerialize(responseFormat)) { return true; } } return false; } public void Serialize(ControllerContext context, object model, ContentType responseFormat) { for (int i = 0; i < this.ResponseFormatHandlers.Count; ++i) { if (this.ResponseFormatHandlers[i].CanSerialize(responseFormat)) { this.ResponseFormatHandlers[i].Serialize(context, model, responseFormat); return; } } throw new NotSupportedException(); } public bool TryMapFormatFriendlyName(string formatName, out ContentType contentType) { for (int i = 0; i < this.ResponseFormatHandlers.Count; ++i) { if (this.ResponseFormatHandlers[i].TryMapFormatFriendlyName(formatName, out contentType)) { return true; } } contentType = null; return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/HtmlHelperExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using System.Web.Mvc.Html; namespace Microsoft.Web.Mvc.Resources { public static class HtmlHelperExtensions { /// /// Generates the Form preamble, defaulting the link for the Retrieve action /// /// /// /// /// public static MvcForm BeginResourceForm(this HtmlHelper html, string controllerName, object routeValues) { return html.BeginResourceForm(controllerName, routeValues, ActionType.Retrieve); } /// /// Generates the Form preamble /// /// /// /// /// /// public static MvcForm BeginResourceForm(this HtmlHelper html, string controllerName, object routeValues, ActionType actionType) { return html.BeginResourceForm(controllerName, routeValues, null, actionType); } /// /// Generates the Form preamble /// /// /// /// /// /// /// public static MvcForm BeginResourceForm(this HtmlHelper html, string controllerName, object routeValues, object htmlAttributes, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return html.BeginRouteForm(controllerName + "-editForm", routeValues, FormMethod.Post, htmlAttributes); case ActionType.GetCreateForm: return html.BeginRouteForm(controllerName + "-createForm", FormMethod.Post, htmlAttributes); case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: return html.BeginRouteForm(controllerName, routeValues, FormMethod.Post, htmlAttributes); case ActionType.Create: return html.BeginRouteForm(controllerName + "-create", FormMethod.Post, htmlAttributes); case ActionType.Index: return html.BeginRouteForm(controllerName + "-index", FormMethod.Post, htmlAttributes); default: throw new ArgumentOutOfRangeException("actionType"); } } /// /// Generates a link to the resource controller, defaulting to the Retrieve action /// /// /// /// /// public static MvcHtmlString ResourceLink(this HtmlHelper html, string controllerName, object routeValues) { return html.ResourceLink(controllerName, controllerName, routeValues, ActionType.Retrieve); } /// /// Generates a link to the resource controller, defaulting to the Retrieve action /// /// /// /// /// /// public static MvcHtmlString ResourceLink(this HtmlHelper html, string controllerName, string linkText, object routeValues) { return html.ResourceLink(controllerName, linkText, routeValues, ActionType.Retrieve); } /// /// Generates a link to the resource controller /// /// /// /// /// /// /// public static MvcHtmlString ResourceLink(this HtmlHelper html, string controllerName, string linkText, object routeValues, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return html.RouteLink(linkText, controllerName + "-editForm", routeValues); case ActionType.GetCreateForm: return html.RouteLink(linkText, controllerName + "-createForm", routeValues); case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: return html.RouteLink(linkText, controllerName, routeValues); case ActionType.Create: return html.RouteLink(linkText, controllerName + "-create", routeValues); case ActionType.Index: return html.RouteLink(linkText, controllerName + "-index", routeValues); default: throw new ArgumentOutOfRangeException("actionType"); } } /// /// Emits a hidden form variable for X-Http-Method-Override. The only valid values for actionType /// are ActionType.Delete and ActionType.Update /// /// /// /// public static MvcHtmlString HttpMethodOverride(this HtmlHelper html, ActionType actionType) { if (actionType != ActionType.Delete && actionType != ActionType.Update) { throw new ArgumentOutOfRangeException("actionType"); } return html.HttpMethodOverride(actionType == ActionType.Delete ? "DELETE" : "PUT"); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/HttpRequestBaseExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Net.Mime; using System.Web; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// HttpRequestBase extension methods that call directly into the DefaultFormatHelper /// public static class HttpRequestBaseExtensions { public static ContentType GetRequestFormat(this HttpRequestBase request) { return DefaultFormatHelper.GetRequestFormat(request, true); } public static IEnumerable GetResponseFormats(this HttpRequestBase request) { return DefaultFormatHelper.GetResponseFormats(request); } internal static bool HasBody(this HttpRequestBase request) { return request.ContentLength > 0 || String.Compare("chunked", request.Headers["Transfer-Encoding"], StringComparison.OrdinalIgnoreCase) == 0; } public static bool IsBrowserRequest(this HttpRequestBase request) { return DefaultFormatHelper.IsBrowserRequest(request); } public static bool IsHttpMethod(this HttpRequestBase request, HttpVerbs httpMethod) { return request.IsHttpMethod(httpMethod, false); } public static bool IsHttpMethod(this HttpRequestBase request, string httpMethod) { return request.IsHttpMethod(httpMethod, false); } // CODEREVIEW: this implementation kind of misses the point of HttpVerbs // by falling back to string comparison, consider something better // also, how do we keep this switch in sync? public static bool IsHttpMethod(this HttpRequestBase request, HttpVerbs httpMethod, bool allowOverride) { switch (httpMethod) { case HttpVerbs.Get: return request.IsHttpMethod("GET", allowOverride); case HttpVerbs.Post: return request.IsHttpMethod("POST", allowOverride); case HttpVerbs.Put: return request.IsHttpMethod("PUT", allowOverride); case HttpVerbs.Delete: return request.IsHttpMethod("DELETE", allowOverride); case HttpVerbs.Head: return request.IsHttpMethod("HEAD", allowOverride); case HttpVerbs.Patch: return request.IsHttpMethod("PATCH", allowOverride); case HttpVerbs.Options: return request.IsHttpMethod("OPTIONS", allowOverride); default: // CODEREVIEW: does this look reasonable? return request.IsHttpMethod(httpMethod.ToString().ToUpperInvariant(), allowOverride); } } public static bool IsHttpMethod(this HttpRequestBase request, string httpMethod, bool allowOverride) { string requestHttpMethod = allowOverride ? request.GetHttpMethodOverride() : request.HttpMethod; return String.Equals(requestHttpMethod, httpMethod, StringComparison.OrdinalIgnoreCase); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/IEnumerableExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Microsoft.Web.Mvc.Resources { public static class IEnumerableExtensions { /// /// Convenience API to allow an IEnumerable{T} (such as returned by Linq2Sql) to be serialized by DataContractSerilizer /// /// /// /// public static IEnumerable AsSerializable(this IEnumerable collection) where T : class { return new IEnumerableWrapper(collection); } // This wrapper allows IEnumerable to be serialized by DataContractSerilizer // it implements the minimal amount of surface needed for serialization. private class IEnumerableWrapper : IEnumerable where T : class { private IEnumerable _collection; // The DataContractSerilizer needs a default constructor to ensure the object can be // deserialized. We have a dummy one since we don't actually need deserialization. public IEnumerableWrapper() { throw new NotImplementedException(); } internal IEnumerableWrapper(IEnumerable collection) { this._collection = collection; } // The DataContractSerilizer needs an Add method to ensure the object can be // deserialized. We have a dummy one since we don't actually need deserialization. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Needed to satisfy the deserialization contract")] [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "item", Justification = "Needed to satisfy the deserialization contract")] public void Add(T item) { throw new NotImplementedException(); } public IEnumerator GetEnumerator() { return this._collection.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this._collection).GetEnumerator(); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/IRequestFormatHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Mime; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// Extensibility mechanism for deserializing data in additional formats. /// FormatManager.Current.RequestFormatHandlers contains the list of request formats /// supported by the web application /// public interface IRequestFormatHandler { /// /// Returns true if the handler can deserialize request's content type /// /// /// bool CanDeserialize(ContentType requestFormat); /// /// Deserialize the request body based on model binding context and return the object. /// Note that the URI parameters are handled by the base infrastructure. /// /// /// /// /// object Deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext, ContentType requestFormat); } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/IResponseFormatHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Mime; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// Extensibility mechanism for serializing response in /// additional formats. FormatManager.Current.RequestFormatHandlers contains the list of request formats /// supported by the web application /// public interface IResponseFormatHandler { /// /// The preferred friendly name for the handled format /// string FriendlyName { get; } /// /// Return true if the specified friendly name ('xml' for instance) can /// be mapped to a content type ('application/xml' for instance). If the mapping /// can be performed return the content type that the friendlyName maps to /// /// /// /// bool TryMapFormatFriendlyName(string friendlyName, out ContentType contentType); /// /// Return true if the specified response format can be serialized /// /// /// bool CanSerialize(ContentType responseFormat); /// /// Serialize the model into the response body in the specified response format /// /// /// /// void Serialize(ControllerContext context, object model, ContentType responseFormat); } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/JsonFormatHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net.Mime; using System.Runtime.Serialization.Json; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { public class JsonFormatHandler : IRequestFormatHandler, IResponseFormatHandler { public string FriendlyName { get { return "Json"; } } public bool CanDeserialize(ContentType requestFormat) { return requestFormat != null && IsCompatibleMediaType(requestFormat.MediaType); } public object Deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext, ContentType requestFormat) { DataContractJsonSerializer json = new DataContractJsonSerializer(bindingContext.ModelType); return json.ReadObject(controllerContext.HttpContext.Request.InputStream); } public bool CanSerialize(ContentType responseFormat) { return responseFormat != null && IsCompatibleMediaType(responseFormat.MediaType); } public void Serialize(ControllerContext context, object model, ContentType responseFormat) { DataContractJsonActionResult json = new DataContractJsonActionResult(model, responseFormat); json.ExecuteResult(context); } protected virtual bool IsCompatibleMediaType(string mediaType) { return (mediaType == "text/json" || mediaType == "application/json"); } public bool TryMapFormatFriendlyName(string friendlyName, out ContentType contentType) { if (String.Equals(friendlyName, this.FriendlyName, StringComparison.OrdinalIgnoreCase)) { contentType = new ContentType("application/json"); return true; } contentType = null; return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/MultiFormatActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using System.Net.Mime; using System.Web; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// Returns the response in the format specified by the request. By default, supports returning the model /// as a HTML view, XML and JSON. /// If the response format requested is not supported, then the NotAcceptable status code is returned /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi", Justification = "FxCop won't accept this in the custom dictionary, so we're suppressing it in source")] public class MultiFormatActionResult : ActionResult { private object _model; private ContentType _responseFormat; private HttpStatusCode _statusCode; public MultiFormatActionResult(object model, ContentType responseFormat) : this(model, responseFormat, HttpStatusCode.OK) { } public MultiFormatActionResult(object model, ContentType responseFormat, HttpStatusCode statusCode) { _model = model; _responseFormat = responseFormat; _statusCode = statusCode; } public override void ExecuteResult(ControllerContext context) { if (!TryExecuteResult(context, this._model, this._responseFormat)) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, this._responseFormat)); } } public virtual bool TryExecuteResult(ControllerContext context, object model, ContentType responseFormat) { if (!FormatManager.Current.CanSerialize(responseFormat)) { return false; } context.HttpContext.Response.ClearContent(); context.HttpContext.Response.StatusCode = (int)_statusCode; FormatManager.Current.Serialize(context, model, responseFormat); return true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/RequestContextExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Mime; using System.Web.Routing; namespace Microsoft.Web.Mvc.Resources { /// /// RequestContext extension methods that call directly into the registered FormatHelper /// public static class RequestContextExtensions { public static ContentType GetRequestFormat(this RequestContext requestContext) { return FormatManager.Current.FormatHelper.GetRequestFormat(requestContext); } public static IEnumerable GetResponseFormats(this RequestContext requestContext) { return FormatManager.Current.FormatHelper.GetResponseFormats(requestContext); } public static bool IsBrowserRequest(this RequestContext requestContext) { return FormatManager.Current.FormatHelper.IsBrowserRequest(requestContext); } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/ResourceControllerFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Net.Mime; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; using System.Web.SessionState; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// Specialized ControllerFactory that augments the base controller factory to make it RESTful - specifically, adding /// support for multiple formats, HTTP method based dispatch to controller methods and HTTP error handling /// public class ResourceControllerFactory : IControllerFactory { private const string RestActionToken = "$REST$"; private IControllerFactory _inner; public ResourceControllerFactory() { _inner = ControllerBuilder.Current.GetControllerFactory(); } public ResourceControllerFactory(IControllerFactory inner) { _inner = inner; } public IController CreateController(RequestContext requestContext, string controllerName) { IController ic = _inner.CreateController(requestContext, controllerName); Controller c = ic as Controller; if (c != null && WebApiEnabledAttribute.IsDefined(c)) { IActionInvoker iai = c.ActionInvoker; ControllerActionInvoker cai = iai as ControllerActionInvoker; if (cai != null) { c.ActionInvoker = new ResourceControllerActionInvoker(); string actionName = requestContext.RouteData.Values["action"] as string; if (String.IsNullOrEmpty(actionName)) { // set it to a well known dummy value to avoid not having an action as that would prevent the fixup // code in ResourceControllerActionInvoker, which is based on ActionDescriptor, from running requestContext.RouteData.Values["action"] = RestActionToken; } } } return ic; } public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { return _inner.GetControllerSessionBehavior(requestContext, controllerName); } public void ReleaseController(IController controller) { _inner.ReleaseController(controller); } // This ActionInvoker allows us to dispatch to a controller when no action was provided by the routing // infrastructure, but the information is available in the request's HTTP verb (GET/PUT/POST/DELETE) private class ResourceControllerActionInvoker : ControllerActionInvoker { protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) { if (actionName == RestActionToken) { // cleanup the restActionToken we set earlier controllerContext.RequestContext.RouteData.Values["action"] = null; List matches = new List(); foreach (ActionDescriptor ad in controllerDescriptor.GetCanonicalActions()) { object[] acceptVerbs = ad.GetCustomAttributes(typeof(AcceptVerbsAttribute), false); if (acceptVerbs.Length > 0) { foreach (object o in acceptVerbs) { AcceptVerbsAttribute ava = o as AcceptVerbsAttribute; if (ava != null) { if (ava.Verbs.Contains(controllerContext.HttpContext.Request.GetHttpMethodOverride().ToUpperInvariant())) { matches.Add(ad); } } } } } switch (matches.Count) { case 0: break; case 1: ActionDescriptor ad = matches[0]; actionName = ad.ActionName; controllerContext.RequestContext.RouteData.Values["action"] = actionName; return ad; default: StringBuilder matchesString = new StringBuilder(matches[0].ActionName); for (int index = 1; index < matches.Count; index++) { matchesString.Append(", "); matchesString.Append(matches[index].ActionName); } return new ResourceErrorActionDescriptor( controllerDescriptor, HttpStatusCode.Conflict, String.Format( CultureInfo.CurrentCulture, MvcResources.ResourceControllerFactory_ConflictingActions, controllerDescriptor.ControllerName, matchesString)); } } return base.FindAction(controllerContext, controllerDescriptor, actionName) ?? new ResourceErrorActionDescriptor( controllerDescriptor, HttpStatusCode.NotFound, String.Format( CultureInfo.CurrentCulture, MvcResources.ResourceControllerFactory_NoActions, controllerDescriptor.ControllerName)); } // This class is used when we don't find an ActionDescriptor or find multiple matches // in this case we want to return an error response but throwing an HttpException from // FindAction bypasses the InvokeExceptionFilters, so instead we throw in this custom ActionDescriptor private class ResourceErrorActionDescriptor : ActionDescriptor { private ControllerDescriptor controllerDescriptor; private string message; private HttpStatusCode statusCode; public ResourceErrorActionDescriptor(ControllerDescriptor controllerDescriptor, HttpStatusCode statusCode, string message) { this.message = message; this.statusCode = statusCode; this.controllerDescriptor = controllerDescriptor; } public override string ActionName { get { return RestActionToken; } } public override ControllerDescriptor ControllerDescriptor { get { return this.controllerDescriptor; } } public override object Execute(ControllerContext controllerContext, IDictionary parameters) { HttpException he = new HttpException((int)this.statusCode, this.message); ResourceErrorActionResult rear; if (!WebApiEnabledAttribute.TryGetErrorResult2(controllerContext.RequestContext, he, out rear)) { rear = new ResourceErrorActionResult(new HttpException((int)this.statusCode, this.message), new ContentType("text/plain")); } return rear; } public override ParameterDescriptor[] GetParameters() { return new ParameterDescriptor[0]; } } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/ResourceErrorActionResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net; using System.Net.Mime; using System.Web; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// Action result for returning HTTP errors that result from performing operations on resources, including /// an optional details in the HTTP body /// public class ResourceErrorActionResult : ActionResult { private object details; private ContentType responseFormat; private HttpStatusCode statusCode; /// /// Sends back a response using the status code in the HttpException. /// The response body contains a details serialized in the responseFormat. /// If the HttpException.Data has a key named "details", its value is used as the response body. /// If there is no such key, HttpException.ToString() is used as the response body. /// /// /// public ResourceErrorActionResult(HttpException httpException, ContentType responseFormat) { this.statusCode = (HttpStatusCode)httpException.GetHttpCode(); this.details = httpException.Data.Contains("details") ? httpException.Data["details"] : httpException.ToString(); this.responseFormat = responseFormat; } public override void ExecuteResult(ControllerContext context) { if (this.details != null) { MultiFormatActionResult rar = new MultiFormatActionResult(this.details, this.responseFormat, this.statusCode); if (rar.TryExecuteResult(context, this.details, this.responseFormat)) { return; } } context.HttpContext.Response.ClearContent(); context.HttpContext.Response.StatusCode = (int)this.statusCode; context.HttpContext.Response.TrySkipIisCustomErrors = true; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/ResourceModelBinder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using System.Net.Mime; using System.Web; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// ModelBinder implementation that augments the inner model binder with support for binding to other formats - /// XML and JSON by default. /// public class ResourceModelBinder : IModelBinder { private IModelBinder _inner; /// /// Wraps the ModelBinders.Binders.DefaultBinder /// public ResourceModelBinder() : this(ModelBinders.Binders.DefaultBinder) { } public ResourceModelBinder(IModelBinder inner) { this._inner = inner; } public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (WebApiEnabledAttribute.IsDefined(controllerContext.Controller)) { if (!controllerContext.RouteData.Values.ContainsKey(bindingContext.ModelName) && controllerContext.HttpContext.Request.HasBody()) { ContentType requestFormat = controllerContext.RequestContext.GetRequestFormat(); object model; if (TryBindModel(controllerContext, bindingContext, requestFormat, out model)) { bindingContext.ModelMetadata.Model = model; MyDefaultModelBinder dmb = new MyDefaultModelBinder(); dmb.CallOnModelUpdated(controllerContext, bindingContext); if (!MyDefaultModelBinder.IsModelValid(bindingContext)) { List details = new List(); foreach (ModelState ms in bindingContext.ModelState.Values) { foreach (ModelError me in ms.Errors) { details.Add(me); } } HttpException failure = new HttpException((int)HttpStatusCode.ExpectationFailed, "Invalid Model"); failure.Data["details"] = details; throw failure; } return model; } throw new HttpException((int)HttpStatusCode.UnsupportedMediaType, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedMediaType, (requestFormat == null ? String.Empty : requestFormat.MediaType))); } } return this._inner.BindModel(controllerContext, bindingContext); } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "This is an existing API; this would be a breaking change")] public bool TryBindModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ContentType requestFormat, out object model) { if (requestFormat != null && String.Compare(requestFormat.MediaType, FormatManager.UrlEncoded, StringComparison.OrdinalIgnoreCase) == 0) { model = this._inner.BindModel(controllerContext, bindingContext); return true; } if (!FormatManager.Current.TryDeserialize(controllerContext, bindingContext, requestFormat, out model)) { model = null; return false; } return true; } private class MyDefaultModelBinder : DefaultModelBinder { public void CallOnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { OnModelUpdated(controllerContext, bindingContext); } internal static new bool IsModelValid(ModelBindingContext bindingContext) { return DefaultModelBinder.IsModelValid(bindingContext); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/ResourceRedirectToRouteResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { /// /// Augments the RedirectToRouteResult behavior by sending Created HTTP status code in responses to POST, OK HTTP status code otherwise /// internal class ResourceRedirectToRouteResult : ActionResult { private RedirectToRouteResult inner; public ResourceRedirectToRouteResult(RedirectToRouteResult inner) { this.inner = inner; } public override void ExecuteResult(ControllerContext context) { // call the base which we expect to be setting the Location header this.inner.ExecuteResult(context); if (!context.RequestContext.IsBrowserRequest()) { // on POST we return Created, otherwise (EG: DELETE) we return OK context.HttpContext.Response.ClearContent(); context.HttpContext.Response.StatusCode = context.HttpContext.Request.IsHttpMethod(HttpVerbs.Post, true) ? (int)HttpStatusCode.Created : (int)HttpStatusCode.OK; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/RouteCollectionExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; using System.Web.Routing; namespace Microsoft.Web.Mvc.Resources { public static class RouteCollectionExtensions { /// /// Adds the routes to enable RESTful routing of requests to specified controller. The controllerName is the URL prefix to the controller. /// The routeSuffix is used for more specific routing in the resource. For example, a controllerName of "books" and a routeSuffix of "{id}" will /// result in the following routes being registered for the controller: /// ~/books/, ~/books/{id} to the resource, /// ~/books/CreateForm to the CreateForm controller action, /// ~/books/{id}/EditForm to the EditForm controller action /// /// /// /// public static void MapResourceRoute(this RouteCollection routes, string controllerName, string routeSuffix) { routes.MapResourceRoute(controllerName, null, routeSuffix, null); } /// /// Adds the routes to enable RESTful routing of requests to specified controller. The controllerName is the URL prefix to the controller. /// The routeSuffix is used for more specific routing in the resource. For example, a controllerName of "books" and a routeSuffix of "{id}" will /// result in the following routes being registered for the controller: /// ~/books/, ~/books/{id} to the resource, /// ~/books/CreateForm to the CreateForm controller action, /// ~/books/{id}/EditForm to the EditForm controller action /// /// /// /// /// public static void MapResourceRoute(this RouteCollection routes, string controllerName, string routeSuffix, object constraints) { routes.MapResourceRoute(controllerName, null, routeSuffix, constraints); } /// /// Adds the routes to enable RESTful routing of requests to specified controller. The routePrefix is the URL prefix to the controller. /// The routeSuffix is used for more specific routing in the resource. For example, a routePrefix of "books" and a routeSuffix of "{id}" will /// result in the following routes being registered for the controller: /// ~/books/, ~/books/{id} to the resource, /// ~/books/CreateForm to the CreateForm controller action, /// ~/books/{id}/EditForm to the EditForm controller action /// /// /// /// /// public static void MapResourceRoute(this RouteCollection routes, string controllerName, string routePrefix, string routeSuffix) { routes.MapResourceRoute(controllerName, routePrefix, routeSuffix, null); } /// /// Adds the routes to enable RESTful routing of requests to specified controller. The routePrefix is the URL prefix to the controller. /// The routeSuffix is used for more specific routing in the resource. For example, a routePrefix of "books" and a routeSuffix of "{id}" will /// result in the following routes being registered for the controller: /// ~/books/, ~/books/{id} to the resource, /// ~/books/CreateForm to the CreateForm controller action, /// ~/books/{id}/EditForm to the EditForm controller action /// /// /// /// /// /// public static void MapResourceRoute(this RouteCollection routes, string controllerName, string routePrefix, string routeSuffix, object constraints) { if (String.IsNullOrEmpty(routePrefix)) { routePrefix = controllerName; } else { routePrefix = routePrefix + "/" + controllerName; } if (!String.IsNullOrEmpty(routeSuffix)) { routeSuffix = "/" + routeSuffix; } routes.MapRoute( controllerName + "-editForm", routePrefix + routeSuffix + "/EditForm", new { controller = controllerName, action = "EditForm" }, constraints); routes.MapRoute( controllerName + "-createForm", routePrefix + "/CreateForm", new { controller = controllerName, action = "CreateForm" }); routes.MapRoute( controllerName, routePrefix + routeSuffix, new { controller = controllerName }, constraints); routes.MapRoute( controllerName + "-create", routePrefix, new { controller = controllerName, action = "Create" }, new { postOnly = new HttpMethodConstraint("POST") }); routes.MapRoute( controllerName + "-index", routePrefix, new { controller = controllerName, action = "Index" }); } [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "controller", Justification = "This is an extension method, the parameter is necessary to provide a place to hook the method")] public static string GetResourceRouteName(this Controller controller, string controllerName, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return controllerName + "-editForm"; case ActionType.GetCreateForm: return controllerName + "-createForm"; case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: return controllerName; case ActionType.Create: return controllerName + "-create"; case ActionType.Index: return controllerName + "-index"; default: throw new ArgumentOutOfRangeException("actionType"); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/UriHelperExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { public static class UriHelperExtensions { /// /// Generates the route URL for the resource controller's Retrieve action /// /// /// /// /// [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters"), SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an Extension Method which allows the user to provide a strongly-typed argument via Expression"), SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Need to be sure the passed-in argument is of type Controller::Action")] public static string ResourceUrl(this UrlHelper url, string controllerName, object routeValues) { return url.ResourceUrl(controllerName, routeValues, ActionType.Retrieve); } /// /// Generates the route URL for the resource /// /// /// /// /// /// [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters"), SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an Extension Method which allows the user to provide a strongly-typed argument via Expression"), SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Need to be sure the passed-in argument is of type Controller::Action")] public static string ResourceUrl(this UrlHelper url, string controllerName, object routeValues, ActionType actionType) { switch (actionType) { case ActionType.GetUpdateForm: return url.RouteUrl(controllerName + "-editForm", routeValues); case ActionType.GetCreateForm: return url.RouteUrl(controllerName + "-createForm"); case ActionType.Retrieve: case ActionType.Delete: case ActionType.Update: return url.RouteUrl(controllerName, routeValues); case ActionType.Create: return url.RouteUrl(controllerName + "-create"); case ActionType.Index: return url.RouteUrl(controllerName + "-index"); default: throw new ArgumentOutOfRangeException("actionType"); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/WebApiEnabledAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Net; using System.Net.Mime; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc.Resources { /// /// Attribute indicating that the controller supports multiple formats (HTML, XML, JSON etc), HTTP method based dispatch /// and HTTP error handling. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This class is designed to be overridden")] public class WebApiEnabledAttribute : ActionFilterAttribute, IExceptionFilter { public WebApiEnabledAttribute() { this.StatusOnNullModel = HttpStatusCode.NotFound; } /// /// The HTTP status code to use in case a null value is returned from the controller action method. /// The default is NotFound /// public HttpStatusCode StatusOnNullModel { get; set; } public static bool IsDefined(ControllerBase controller) { Type controllerType = controller.GetType(); WebApiEnabledAttribute[] rea = controllerType.GetCustomAttributes(typeof(WebApiEnabledAttribute), true) as WebApiEnabledAttribute[]; return rea != null && rea.Length > 0; } public override void OnActionExecuted(ActionExecutedContext filterContext) { MultiFormatActionResult multiFormatResult = filterContext.Result as MultiFormatActionResult; if (multiFormatResult == null) { ViewResultBase viewResult = filterContext.Result as ViewResultBase; if (viewResult != null && viewResult.ViewData != null) { bool handled = false; foreach (ContentType responseFormat in filterContext.RequestContext.GetResponseFormats()) { // CONSIDER: making this lookup optional if perf is an issue for (int i = 0; i < FormatManager.Current.ResponseFormatHandlers.Count; ++i) { IResponseFormatHandler handler = FormatManager.Current.ResponseFormatHandlers[i]; if (handler.CanSerialize(responseFormat)) { // we can't use the full ContentType's name (EG: "text/xml") // instead we use the FriendlyName on the matching IResponseFormatHandler string friendlyName = handler.FriendlyName; string viewName = viewResult.ViewName; if (String.IsNullOrEmpty(viewName)) { viewName = filterContext.RouteData.GetRequiredString("action"); } // CONSIDER: is this naming convention sufficient? look at extensibility (how can I customize the FindView process?) viewName = viewName + "." + friendlyName; // CONSIDER: ViewEngineCollection queries view engines in registration order and returns 1st match, // would it make sense to let the client provide a hint in case ViewEngineResult result = viewResult.ViewEngineCollection.FindView(filterContext, viewName, null); // ignore errors and fallback to default behavior if (result != null && result.View != null) { Encoding encoding = Encoding.UTF8; if (!String.IsNullOrEmpty(responseFormat.CharSet)) { try { encoding = Encoding.GetEncoding(responseFormat.CharSet); } catch (ArgumentException) { throw new HttpException((int)HttpStatusCode.NotAcceptable, String.Format(CultureInfo.CurrentCulture, MvcResources.Resources_UnsupportedFormat, responseFormat)); } } responseFormat.CharSet = encoding.HeaderName; filterContext.HttpContext.Response.ContentType = responseFormat.ToString(); filterContext.HttpContext.Response.ContentEncoding = encoding; // we have set the Response.ContentType but know that the webforms view engine will override it // a different ViewPage base class that sets this can be used to workaround this // so we make the computed responseFormat available in ViewData viewResult.ViewData[DefaultFormatHelper.ResponseFormatKey] = responseFormat; viewResult.View = result.View; viewResult.ViewName = viewName; handled = true; break; } } } if (handled) { break; } if (TryGetResult(viewResult, responseFormat, out multiFormatResult)) { if (multiFormatResult != null) { filterContext.Result = multiFormatResult; } handled = true; break; } } if (!handled) { // if enumeration doesn't yield a handler the request is not acceptable // CONSIDER: returning all formats considered in the exception messages throw new HttpException((int)HttpStatusCode.NotAcceptable, "None of the formats specified by the accept header is supported."); } } } base.OnActionExecuted(filterContext); RedirectToRouteResult redirectResult = filterContext.Result as RedirectToRouteResult; if (redirectResult != null && !filterContext.RequestContext.IsBrowserRequest()) { filterContext.Result = new ResourceRedirectToRouteResult(redirectResult); } } public void OnException(ExceptionContext filterContext) { if (filterContext.ExceptionHandled) { return; } HttpException he = filterContext.Exception as HttpException; if (he != null) { ResourceErrorActionResult rear; if (TryGetErrorResult2(filterContext.RequestContext, he, out rear)) { if (rear != null) { filterContext.Result = rear; filterContext.ExceptionHandled = true; } return; } // if enumeration doesn't yield a handler the request is not acceptable // CONSIDER: returning all formats considered in the exception messages throw new HttpException((int)HttpStatusCode.NotAcceptable, "None of the formats specified by the accept header is supported."); } } public virtual bool TryGetErrorResult(HttpException exception, ContentType responseFormat, out ResourceErrorActionResult actionResult) { if (FormatManager.Current.CanSerialize(responseFormat)) { actionResult = new ResourceErrorActionResult(exception, responseFormat); return true; } switch (responseFormat.MediaType) { case "application/octet-stream": case "application/x-www-form-urlencoded": case "text/html": case "*/*": actionResult = null; return true; default: actionResult = null; return false; } } public virtual bool TryGetResult(ViewResultBase viewResult, ContentType responseFormat, out MultiFormatActionResult actionResult) { if (FormatManager.Current.CanSerialize(responseFormat)) { if (viewResult.ViewData.Model == null) { throw new HttpException((int)this.StatusOnNullModel, this.StatusOnNullModel.ToString()); } actionResult = new MultiFormatActionResult(viewResult.ViewData.Model, responseFormat); return true; } switch (responseFormat.MediaType) { case "application/octet-stream": case "application/x-www-form-urlencoded": case "text/html": case "*/*": actionResult = null; return true; default: actionResult = null; return false; } } internal static bool TryGetErrorResult2(RequestContext requestContext, HttpException he, out ResourceErrorActionResult actionResult) { foreach (ContentType responseFormat in requestContext.GetResponseFormats()) { WebApiEnabledAttribute dummy = new WebApiEnabledAttribute(); if (dummy.TryGetErrorResult(he, responseFormat, out actionResult)) { return true; } } actionResult = null; return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/Resources/XmlFormatHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net.Mime; using System.Runtime.Serialization; using System.Web.Mvc; namespace Microsoft.Web.Mvc.Resources { public class XmlFormatHandler : IRequestFormatHandler, IResponseFormatHandler { public string FriendlyName { get { return "Xml"; } } public bool CanDeserialize(ContentType requestFormat) { return requestFormat != null && IsCompatibleMediaType(requestFormat.MediaType); } public object Deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext, ContentType requestFormat) { DataContractSerializer xml = new DataContractSerializer(bindingContext.ModelType, null, Int32.MaxValue, true, true, null); return xml.ReadObject(controllerContext.HttpContext.Request.InputStream); } public bool CanSerialize(ContentType responseFormat) { return responseFormat != null && IsCompatibleMediaType(responseFormat.MediaType); } public void Serialize(ControllerContext context, object model, ContentType responseFormat) { DataContractXmlActionResult xml = new DataContractXmlActionResult(model, responseFormat); xml.ExecuteResult(context); } protected virtual bool IsCompatibleMediaType(string mediaType) { return (mediaType == "text/xml" || mediaType == "application/xml"); } public bool TryMapFormatFriendlyName(string friendlyName, out ContentType contentType) { if (String.Equals(friendlyName, this.FriendlyName, StringComparison.OrdinalIgnoreCase)) { contentType = new ContentType("application/xml"); return true; } contentType = null; return false; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ScriptExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public static class ScriptExtensions { public static MvcHtmlString Script(this HtmlHelper helper, string releaseFile) { return Script(helper, releaseFile, releaseFile); } public static MvcHtmlString Script(this HtmlHelper helper, string releaseFile, string debugFile) { if (String.IsNullOrEmpty(releaseFile)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "releaseFile"); } if (String.IsNullOrEmpty(debugFile)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "debugFile"); } string src; string file = helper.ViewContext.HttpContext.IsDebuggingEnabled ? debugFile : releaseFile; if (IsRelativeToDefaultPath(file)) { src = "~/Scripts/" + file; } else { src = file; } TagBuilder scriptTag = new TagBuilder("script"); scriptTag.MergeAttribute("type", "text/javascript"); scriptTag.MergeAttribute("src", UrlHelper.GenerateContentUrl(src, helper.ViewContext.HttpContext)); return MvcHtmlString.Create(scriptTag.ToString(TagRenderMode.Normal)); } internal static bool IsRelativeToDefaultPath(string file) { return !(file.StartsWith("~", StringComparison.Ordinal) || file.StartsWith("../", StringComparison.Ordinal) || file.StartsWith("/", StringComparison.Ordinal) || file.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || file.StartsWith("https://", StringComparison.OrdinalIgnoreCase)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/SerializationExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { public static class SerializationExtensions { public static MvcHtmlString Serialize(this HtmlHelper htmlHelper, string name) { return SerializeInternal(htmlHelper, name, null, useViewData: true); } internal static MvcHtmlString Serialize(this HtmlHelper htmlHelper, string name, MvcSerializer serializer) { return SerializeInternal(htmlHelper, name, null, useViewData: true, serializer: serializer); } public static MvcHtmlString Serialize(this HtmlHelper htmlHelper, string name, object data) { return SerializeInternal(htmlHelper, name, data, useViewData: false); } internal static MvcHtmlString Serialize(this HtmlHelper htmlHelper, string name, object data, MvcSerializer serializer) { return SerializeInternal(htmlHelper, name, data, useViewData: false, serializer: serializer); } private static MvcHtmlString SerializeInternal(HtmlHelper htmlHelper, string name, object data, bool useViewData) { return SerializeInternal(htmlHelper, name, data, useViewData, null); } private static MvcHtmlString SerializeInternal(HtmlHelper htmlHelper, string name, object data, bool useViewData, MvcSerializer serializer) { if (htmlHelper == null) { throw new ArgumentNullException("htmlHelper"); } if (String.IsNullOrEmpty(name)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name"); } name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); if (useViewData) { data = htmlHelper.ViewData.Eval(name); } string serializedData = (serializer ?? new MvcSerializer()).Serialize(data); TagBuilder builder = new TagBuilder("input"); builder.Attributes["type"] = "hidden"; builder.Attributes["name"] = name; builder.Attributes["value"] = serializedData; return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/ServerVariablesValueProviderFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Globalization; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class ServerVariablesValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { NameValueCollection nvc = controllerContext.HttpContext.Request.ServerVariables; return new NameValueCollectionValueProvider(nvc, CultureInfo.InvariantCulture); } } } ================================================ FILE: src/Microsoft.Web.Mvc/SessionValueProviderFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.Web; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class SessionValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { HttpSessionStateBase session = controllerContext.HttpContext.Session; if (session == null) { // session is disabled return null; } Dictionary backingStore = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (string key in session) { if (key != null) { backingStore[key] = session[key]; // copy to backing store } } // use the invariant culture since Session contains serialized objects return new DictionaryValueProvider(backingStore, CultureInfo.InvariantCulture); } } } ================================================ FILE: src/Microsoft.Web.Mvc/SkipBindingAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public sealed class SkipBindingAttribute : CustomModelBinderAttribute { private static readonly NullBinder _nullBinder = new NullBinder(); public override IModelBinder GetBinder() { return _nullBinder; } private class NullBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { return null; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/TempDataValueProviderFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.Web.Mvc; namespace Microsoft.Web.Mvc { public class TempDataValueProviderFactory : ValueProviderFactory { public override IValueProvider GetValueProvider(ControllerContext controllerContext) { TempDataDictionary tempData = controllerContext.Controller.TempData; if (tempData.Count == 0) { // fast-track empty TempData return null; } return new TempDataValueProvider(tempData); } /// /// dummy struct that resembles Void but can be used in a generic context /// private struct TempDataVoid { } private sealed class TempDataValueProvider : DictionaryValueProvider { private readonly TempDataDictionary _tempData; // use invariant culture since TempData should contain objects public TempDataValueProvider(TempDataDictionary tempData) : base(GetVoidDictionary(tempData), CultureInfo.InvariantCulture) { _tempData = tempData; } public override ValueProviderResult GetValue(string key) { object rawValue; // TryGetValue will mark the entry for removal. if (_tempData.TryGetValue(key, out rawValue)) { string attemptedValue = Convert.ToString(rawValue, CultureInfo.InvariantCulture); return new ValueProviderResult(rawValue, attemptedValue, CultureInfo.InvariantCulture); } else { // no value found return null; } } private static Dictionary GetVoidDictionary(TempDataDictionary tempData) { // Create a special backing store that doesn't directly hold the values, since the DictionaryValueProvider // enumerates over the backing store but enumerating over TempData marks everything for removal. Dictionary d = new Dictionary(StringComparer.OrdinalIgnoreCase); // Enumerating over TempDataDictionary.Keys doesn't mark them for removal. foreach (string key in tempData.Keys) { d[key] = default(TempDataVoid); } return d; } } } } ================================================ FILE: src/Microsoft.Web.Mvc/TypeDescriptorHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Web.Mvc; namespace Microsoft.Web.Mvc { internal static class TypeDescriptorHelper { private static readonly MockMetadataProvider _mockMetadataProvider = new MockMetadataProvider(); public static ICustomTypeDescriptor Get(Type type) { return _mockMetadataProvider.GetTypeDescriptor(type); } // System.Web.Mvc.TypeDescriptorHelpers is internal, so this mock subclassed type provides // access to it via the GetTypeDescriptor() virtual method. private sealed class MockMetadataProvider : AssociatedMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable attributes, Type containerType, Func modelAccessor, Type modelType, string propertyName) { throw new NotImplementedException(); } public new ICustomTypeDescriptor GetTypeDescriptor(Type type) { return base.GetTypeDescriptor(type); } } } } ================================================ FILE: src/Microsoft.Web.Mvc/TypeHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; namespace Microsoft.Web.Mvc { internal static class TypeHelpers { public static Type ExtractGenericInterface(Type queryType, Type interfaceType) { Func matchesInterface = t => t.IsGenericType && t.GetGenericTypeDefinition() == interfaceType; return (matchesInterface(queryType)) ? queryType : queryType.GetInterfaces().FirstOrDefault(matchesInterface); } public static Type[] GetTypeArgumentsIfMatch(Type closedType, Type matchingOpenType) { if (!closedType.IsGenericType) { return null; } Type openType = closedType.GetGenericTypeDefinition(); return (matchingOpenType == openType) ? closedType.GetGenericArguments() : null; } public static bool IsCompatibleObject(Type type, object value) { return ((value == null && TypeAllowsNullValue(type)) || type.IsInstanceOfType(value)); } public static bool IsNullableValueType(Type type) { return Nullable.GetUnderlyingType(type) != null; } public static bool TypeAllowsNullValue(Type type) { return (!type.IsValueType || IsNullableValueType(type)); } } } ================================================ FILE: src/Microsoft.Web.Mvc/UrlAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; using System.Web.Mvc; using Microsoft.Web.Mvc.Properties; namespace Microsoft.Web.Mvc { [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class UrlAttribute : DataTypeAttribute, IClientValidatable { private static Regex _regex = new Regex(@"^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); public UrlAttribute() : base(DataType.Url) { ErrorMessage = MvcResources.UrlAttribute_Invalid; } public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new ModelClientValidationRule { ValidationType = "url", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; } public override bool IsValid(object value) { if (value == null) { return true; } string valueAsString = value as string; return valueAsString != null && _regex.Match(valueAsString).Length > 0; } } } ================================================ FILE: src/Microsoft.Web.Mvc/ValueProviderUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.Web.Mvc { internal static class ValueProviderUtil { public static bool IsPrefixMatch(string prefix, string testString) { if (testString == null) { return false; } if (prefix.Length == 0) { return true; // shortcut - non-null testString matches empty prefix } if (!testString.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { return false; // prefix doesn't match } if (testString.Length == prefix.Length) { return true; // exact match } // invariant: testString.Length > prefix.Length switch (testString[prefix.Length]) { case '.': case '[': return true; // known delimiters default: return false; // not known delimiter } } } } ================================================ FILE: src/Microsoft.Web.Mvc/ViewExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; using ExpressionHelper = Microsoft.Web.Mvc.Internal.ExpressionHelper; namespace Microsoft.Web.Mvc { public static class ViewExtensions { public static void RenderRoute(this HtmlHelper helper, RouteValueDictionary routeValues) { if (routeValues == null) { throw new ArgumentNullException("routeValues"); } string actionName = (string)routeValues["action"]; helper.RenderAction(actionName, routeValues); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public static void RenderAction(this HtmlHelper helper, Expression> action) where TController : Controller { RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(action); foreach (var entry in helper.ViewContext.RouteData.Values) { if (!rvd.ContainsKey(entry.Key)) { rvd.Add(entry.Key, entry.Value); } } RenderRoute(helper, rvd); } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/AuthenticationClientData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using DotNetOpenAuth.AspNet; namespace Microsoft.Web.WebPages.OAuth { /// /// Store display name and extra data of an IAuthenticationClient instance. /// public class AuthenticationClientData { /// /// Initializes a new instance of the class. /// /// The authentication client. /// The display name. /// The data bag used to store extra data about this client public AuthenticationClientData(IAuthenticationClient authenticationClient, string displayName, IDictionary extraData) { if (authenticationClient == null) { throw new ArgumentNullException("authenticationClient"); } AuthenticationClient = authenticationClient; DisplayName = displayName; ExtraData = extraData; } /// /// Gets the authentication client. /// public IAuthenticationClient AuthenticationClient { get; private set; } /// /// Gets the display name. /// public string DisplayName { get; private set; } /// /// The data bag used to store extra data about this client. /// public IDictionary ExtraData { get; private set; } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Web.WebPages.OAuth")] ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Microsoft.Web.WebPages.OAuth.csproj ================================================  {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853} Library Properties Microsoft.Web.WebPages.OAuth Microsoft.Web.WebPages.OAuth $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES False ..\..\packages\DotNetOpenAuth.AspNet.4.0.3.12153\lib\net40-full\DotNetOpenAuth.AspNet.dll False ..\..\packages\DotNetOpenAuth.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.Core.dll False ..\..\packages\DotNetOpenAuth.OAuth.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OAuth.dll False ..\..\packages\DotNetOpenAuth.OAuth.Consumer.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OAuth.Consumer.dll False ..\..\packages\DotNetOpenAuth.OpenId.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OpenId.dll False ..\..\packages\DotNetOpenAuth.OpenId.RelyingParty.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OpenId.RelyingParty.dll Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True WebResources.resx True True OAuthResources.resx {8F18041B-9410-4C36-A9C5-067813DF5F31} System.Web.Razor {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} System.Web.WebPages.Razor {55A15F40-1435-4248-A7F2-2A146BB83586} WebMatrix.WebData Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator WebResources.Designer.cs ResXFileCodeGenerator OAuthResources.Designer.cs ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/OAuthAccount.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using Microsoft.Web.WebPages.OAuth.Properties; namespace Microsoft.Web.WebPages.OAuth { /// /// Represents an OAuth & OpenID account. /// public class OAuthAccount { /// /// Initializes a new instance of the class. /// /// The provider. /// The provider user id. public OAuthAccount(string provider, string providerUserId) { if (String.IsNullOrEmpty(provider)) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, WebResources.Argument_Cannot_Be_Null_Or_Empty, "provider"), "provider"); } if (String.IsNullOrEmpty(providerUserId)) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, WebResources.Argument_Cannot_Be_Null_Or_Empty, "providerUserId"), "providerUserId"); } Provider = provider; ProviderUserId = providerUserId; } /// /// Gets the provider name. /// public string Provider { get; private set; } /// /// Gets the provider user id. /// public string ProviderUserId { get; private set; } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/OAuthWebSecurity.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Web; using System.Web.Security; using DotNetOpenAuth.AspNet; using DotNetOpenAuth.AspNet.Clients; using Microsoft.Web.WebPages.OAuth.Properties; using WebMatrix.WebData; namespace Microsoft.Web.WebPages.OAuth { /// /// Contains APIs to manage authentication against OAuth & OpenID service providers /// public static class OAuthWebSecurity { internal static IOpenAuthDataProvider OAuthDataProvider = new WebPagesOAuthDataProvider(); // contains all registered authentication clients private static readonly Dictionary _authenticationClients = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Gets a value indicating whether the current user is authenticated by an OAuth provider. /// public static bool IsAuthenticatedWithOAuth { get { if (HttpContext.Current == null) { throw new InvalidOperationException(WebResources.HttpContextNotAvailable); } return GetIsAuthenticatedWithOAuthCore(new HttpContextWrapper(HttpContext.Current)); } } /// /// Gets the collection of all registered authentication client; /// /// public static ICollection RegisteredClientData { get { // the Values property returns a read-only collection. // so we don't need to worry about clients of this method modifying our internal collection. return _authenticationClients.Values; } } /// /// Registers the Facebook client. /// /// The app id. /// The app secret. public static void RegisterFacebookClient(string appId, string appSecret) { RegisterFacebookClient(appId, appSecret, displayName: "Facebook"); } /// /// Registers the Facebook client. /// /// The app id. /// The app secret. /// The display name of the client. public static void RegisterFacebookClient(string appId, string appSecret, string displayName) { RegisterFacebookClient(appId, appSecret, displayName, extraData: new Dictionary()); } /// /// Registers the Facebook client. /// /// The app id. /// The app secret. /// The display name. /// The data bag used to store extra data about this client public static void RegisterFacebookClient(string appId, string appSecret, string displayName, IDictionary extraData) { RegisterClient(new FacebookClient(appId, appSecret), displayName, extraData); } /// /// Registers the Microsoft account client. /// /// The client id. /// The client secret. public static void RegisterMicrosoftClient(string clientId, string clientSecret) { RegisterMicrosoftClient(clientId, clientSecret, displayName: "Microsoft"); } /// /// Registers the Microsoft account client. /// /// The client id. /// The client secret. /// The display name. public static void RegisterMicrosoftClient(string clientId, string clientSecret, string displayName) { RegisterMicrosoftClient(clientId, clientSecret, displayName, new Dictionary()); } /// /// Registers the Microsoft account client. /// /// The client id. /// The client secret. /// The display name. /// The data bag used to store extra data about this client public static void RegisterMicrosoftClient(string clientId, string clientSecret, string displayName, IDictionary extraData) { RegisterClient(new MicrosoftClient(clientId, clientSecret), displayName, extraData); } /// /// Registers the Twitter client. /// /// The consumer key. /// The consumer secret. public static void RegisterTwitterClient(string consumerKey, string consumerSecret) { RegisterTwitterClient(consumerKey, consumerSecret, displayName: "Twitter"); } /// /// Registers the Twitter client. /// /// The consumer key. /// The consumer secret. /// The display name. public static void RegisterTwitterClient(string consumerKey, string consumerSecret, string displayName) { RegisterTwitterClient(consumerKey, consumerSecret, displayName, new Dictionary()); } /// /// Registers the Twitter client. /// /// The consumer key. /// The consumer secret. /// The display name. /// The data bag used to store extra data about this client public static void RegisterTwitterClient(string consumerKey, string consumerSecret, string displayName, IDictionary extraData) { var twitterClient = new TwitterClient(consumerKey, consumerSecret); RegisterClient(twitterClient, displayName, extraData); } /// /// Registers the LinkedIn client. /// /// The consumer key. /// The consumer secret. public static void RegisterLinkedInClient(string consumerKey, string consumerSecret) { RegisterLinkedInClient(consumerKey, consumerSecret, displayName: "LinkedIn"); } /// /// Registers the LinkedIn client. /// /// The consumer key. /// The consumer secret. /// The display name. public static void RegisterLinkedInClient(string consumerKey, string consumerSecret, string displayName) { RegisterLinkedInClient(consumerKey, consumerSecret, displayName, new Dictionary()); } /// /// Registers the LinkedIn client. /// /// The consumer key. /// The consumer secret. /// The display name. /// The data bag used to store extra data about this client public static void RegisterLinkedInClient(string consumerKey, string consumerSecret, string displayName, IDictionary extraData) { var linkedInClient = new LinkedInClient(consumerKey, consumerSecret); RegisterClient(linkedInClient, displayName, extraData); } /// /// Registers the Google client. /// public static void RegisterGoogleClient() { RegisterGoogleClient(displayName: "Google"); } /// /// Registers the Google client. /// /// The display name. public static void RegisterGoogleClient(string displayName) { RegisterClient(new GoogleOpenIdClient(), displayName, new Dictionary()); } /// /// Registers the Google client. /// /// The display name. /// The data bag. public static void RegisterGoogleClient(string displayName, IDictionary extraData) { RegisterClient(new GoogleOpenIdClient(), displayName, extraData); } /// /// Registers the Yahoo client. /// public static void RegisterYahooClient() { RegisterYahooClient(displayName: "Yahoo"); } /// /// Registers the Yahoo client. /// /// The display name. public static void RegisterYahooClient(string displayName) { RegisterYahooClient(displayName, new Dictionary()); } /// /// Registers the Yahoo client. /// /// The display name. /// The data bag. public static void RegisterYahooClient(string displayName, IDictionary extraData) { RegisterClient(new YahooOpenIdClient(), displayName, extraData); } /// /// Registers an authentication client. /// /// The client to be registered. [CLSCompliant(false)] public static void RegisterClient(IAuthenticationClient client) { RegisterClient(client, displayName: null, extraData: new Dictionary()); } /// /// Registers an authentication client. /// /// The client to be registered /// The display name. /// The data bag used to store extra data about the specified client [CLSCompliant(false)] public static void RegisterClient(IAuthenticationClient client, string displayName, IDictionary extraData) { if (client == null) { throw new ArgumentNullException("client"); } if (String.IsNullOrEmpty(client.ProviderName)) { throw new ArgumentException(WebResources.InvalidServiceProviderName, "client"); } if (_authenticationClients.ContainsKey(client.ProviderName)) { throw new ArgumentException(WebResources.ServiceProviderNameExists, "client"); } var clientData = new AuthenticationClientData(client, displayName, extraData); _authenticationClients.Add(client.ProviderName, clientData); } /// /// Requests the specified provider to start the authentication by directing users to an external website /// /// The provider. public static void RequestAuthentication(string provider) { RequestAuthentication(provider, returnUrl: null); } /// /// Requests the specified provider to start the authentication by directing users to an external website /// /// The provider. /// The return url after user is authenticated. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to allow relative app path, and support ~/")] public static void RequestAuthentication(string provider, string returnUrl) { if (HttpContext.Current == null) { throw new InvalidOperationException(WebResources.HttpContextNotAvailable); } RequestAuthenticationCore(new HttpContextWrapper(HttpContext.Current), provider, returnUrl); } /// /// Requests the authentication core. /// /// The context. /// The provider. /// The return URL. internal static void RequestAuthenticationCore(HttpContextBase context, string provider, string returnUrl) { IAuthenticationClient client = GetOAuthClient(provider); var securityManager = new OpenAuthSecurityManager(context, client, OAuthDataProvider); securityManager.RequestAuthentication(returnUrl); } /// /// Checks if user is successfully authenticated when user is redirected back to this user. /// [CLSCompliant(false)] public static AuthenticationResult VerifyAuthentication() { return VerifyAuthentication(returnUrl: null); } /// /// Checks if user is successfully authenticated when user is redirected back to this user. /// /// The return URL which must match the one passed to RequestAuthentication earlier. [CLSCompliant(false)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#", Justification = "We want to allow relative app path, and support ~/")] public static AuthenticationResult VerifyAuthentication(string returnUrl) { if (HttpContext.Current == null) { throw new InvalidOperationException(WebResources.HttpContextNotAvailable); } return VerifyAuthenticationCore(new HttpContextWrapper(HttpContext.Current), returnUrl); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to allow relative app path, and support ~/")] internal static AuthenticationResult VerifyAuthenticationCore(HttpContextBase context, string returnUrl) { string providerName = OpenAuthSecurityManager.GetProviderName(context); if (String.IsNullOrEmpty(providerName)) { return AuthenticationResult.Failed; } IAuthenticationClient client; if (TryGetOAuthClient(providerName, out client)) { var securityManager = new OpenAuthSecurityManager(context, client, OAuthDataProvider); return securityManager.VerifyAuthentication(returnUrl); } else { throw new InvalidOperationException(WebResources.InvalidServiceProviderName); } } /// /// Checks if the specified provider user id represents a valid account. /// If it does, log user in. /// /// Name of the provider. /// The provider user id. /// if set to true create persistent cookie as part of the login. /// /// true if the login is successful. /// [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "Login is used more consistently in ASP.Net")] public static bool Login(string providerName, string providerUserId, bool createPersistentCookie) { if (HttpContext.Current == null) { throw new InvalidOperationException(WebResources.HttpContextNotAvailable); } return LoginCore(new HttpContextWrapper(HttpContext.Current), providerName, providerUserId, createPersistentCookie); } internal static bool LoginCore(HttpContextBase context, string providerName, string providerUserId, bool createPersistentCookie) { var provider = GetOAuthClient(providerName); var securityManager = new OpenAuthSecurityManager(context, provider, OAuthDataProvider); return securityManager.Login(providerUserId, createPersistentCookie); } internal static bool GetIsAuthenticatedWithOAuthCore(HttpContextBase context) { return new OpenAuthSecurityManager(context).IsAuthenticatedWithOpenAuth; } /// /// Creates or update the account with the specified provider, provider user id and associate it with the specified user name. /// /// Name of the provider. /// The provider user id. /// The user name. public static void CreateOrUpdateAccount(string providerName, string providerUserId, string userName) { ExtendedMembershipProvider provider = VerifyProvider(); provider.CreateOrUpdateOAuthAccount(providerName, providerUserId, userName); } /// /// Gets the registered user name corresponding to the specified provider and provider user id. /// /// Name of the provider. /// The provider user id. /// public static string GetUserName(string providerName, string providerUserId) { return OAuthDataProvider.GetUserNameFromOpenAuth(providerName, providerUserId); } /// /// Gets all OAuth & OpenID accounts which are associted with the specified user name. /// /// The user name. public static ICollection GetAccountsFromUserName(string userName) { if (String.IsNullOrEmpty(userName)) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, WebResources.Argument_Cannot_Be_Null_Or_Empty, "userName"), "userName"); } ExtendedMembershipProvider provider = VerifyProvider(); return provider.GetAccountsForUser(userName).Select(p => new OAuthAccount(p.Provider, p.ProviderUserId)).ToList(); } /// /// Determines whether there exists a local account (as opposed to OAuth account) with the specified userId. /// /// The user id to check for local account. /// /// true if there is a local account with the specified user id]; otherwise, false. /// public static bool HasLocalAccount(int userId) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.HasLocalAccount(userId); } /// /// Delete the specified OAuth & OpenID account /// /// Name of the provider. /// The provider user id. public static bool DeleteAccount(string providerName, string providerUserId) { ExtendedMembershipProvider provider = VerifyProvider(); string username = GetUserName(providerName, providerUserId); if (String.IsNullOrEmpty(username)) { // account doesn't exist return false; } provider.DeleteOAuthAccount(providerName, providerUserId); return true; } /// /// Gets the OAuth client data of the specified provider name. /// /// Name of the provider. /// The AuthenticationClientData of the specified provider name. public static AuthenticationClientData GetOAuthClientData(string providerName) { if (providerName == null) { throw new ArgumentNullException("providerName"); } return _authenticationClients[providerName]; } /// /// Tries getting the OAuth client data of the specified provider name. /// /// Name of the provider. /// The client data of the specified provider name. /// true if the client data is found for the specified provider name. Otherwise, false public static bool TryGetOAuthClientData(string providerName, out AuthenticationClientData clientData) { if (providerName == null) { throw new ArgumentNullException("providerName"); } return _authenticationClients.TryGetValue(providerName, out clientData); } internal static IAuthenticationClient GetOAuthClient(string providerName) { if (!_authenticationClients.ContainsKey(providerName)) { throw new ArgumentException(WebResources.ServiceProviderNotFound, "providerName"); } return _authenticationClients[providerName].AuthenticationClient; } internal static bool TryGetOAuthClient(string provider, out IAuthenticationClient client) { if (_authenticationClients.ContainsKey(provider)) { client = _authenticationClients[provider].AuthenticationClient; return true; } else { client = null; return false; } } /// /// for unit tests /// internal static void ClearProviders() { _authenticationClients.Clear(); } private static ExtendedMembershipProvider VerifyProvider() { var provider = Membership.Provider as ExtendedMembershipProvider; if (provider == null) { throw new InvalidOperationException(); } return provider; } /// /// Securely serializes a providerName/providerUserId pair. /// /// The provider name. /// The provider-specific user id. /// A cryptographically protected serialization of the inputs which is suitable for round-tripping. /// Do not persist the return value to permanent storage. This implementation is subject to change. public static string SerializeProviderUserId(string providerName, string providerUserId) { if (providerName == null) { throw new ArgumentNullException("providerName"); } if (providerUserId == null) { throw new ArgumentNullException("providerUserId"); } return ProviderUserIdSerializationHelper.ProtectData(providerName, providerUserId); } /// /// Deserializes a string obtained from back into a /// providerName/providerUserId pair. /// /// The input data. /// Will contain the deserialized provider name. /// Will contain the deserialized provider user id. /// True if successful. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "This design is acceptable")] public static bool TryDeserializeProviderUserId(string data, out string providerName, out string providerUserId) { if (data == null) { throw new ArgumentNullException("data"); } return ProviderUserIdSerializationHelper.UnprotectData(data, out providerName, out providerUserId); } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/PreApplicationStartCode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.WebPages.Razor; namespace Microsoft.Web.WebPages.OAuth { /// /// Defines Start() method that gets executed when this assembly is loaded by ASP.NET /// [EditorBrowsable(EditorBrowsableState.Never)] public static class PreApplicationStartCode { /// /// Register global namepace imports for this assembly /// public static void Start() { WebPageRazorHost.AddGlobalImport("DotNetOpenAuth.AspNet"); WebPageRazorHost.AddGlobalImport("Microsoft.Web.WebPages.OAuth"); // Disable the "calls home" feature of DNOA DotNetOpenAuth.Reporting.Enabled = false; } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; [assembly: AssemblyTitle("Microsoft.Web.WebPages.OAuth")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("Microsoft.Web.WebPages.OAuth.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: PreApplicationStartMethod(typeof(Microsoft.Web.WebPages.OAuth.PreApplicationStartCode), "Start")] ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Properties/WebResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.488 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.Web.WebPages.OAuth.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class WebResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal WebResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Web.WebPages.OAuth.Properties.WebResources", typeof(WebResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to {0} cannot be null or an empty string.. /// internal static string Argument_Cannot_Be_Null_Or_Empty { get { return ResourceManager.GetString("Argument_Cannot_Be_Null_Or_Empty", resourceCulture); } } /// /// Looks up a localized string similar to HttpContext is not available in the current thread.. /// internal static string HttpContextNotAvailable { get { return ResourceManager.GetString("HttpContextNotAvailable", resourceCulture); } } /// /// Looks up a localized string similar to Invalid provider name.. /// internal static string InvalidServiceProviderName { get { return ResourceManager.GetString("InvalidServiceProviderName", resourceCulture); } } /// /// Looks up a localized string similar to Another service provider with the same name has already been registered.. /// internal static string ServiceProviderNameExists { get { return ResourceManager.GetString("ServiceProviderNameExists", resourceCulture); } } /// /// Looks up a localized string similar to A service provider could not be found by the specified name.. /// internal static string ServiceProviderNotFound { get { return ResourceManager.GetString("ServiceProviderNotFound", resourceCulture); } } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Properties/WebResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 {0} cannot be null or an empty string. HttpContext is not available in the current thread. Invalid provider name. Another service provider with the same name has already been registered. A service provider could not be found by the specified name. ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/ProviderUserIdSerializationHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Web.Security; namespace Microsoft.Web.WebPages.OAuth { internal static class ProviderUserIdSerializationHelper { // Custom message purpose to prevent this data from being readable by a different subsystem. private static byte[] _padding = new byte[] { 0x85, 0xC5, 0x65, 0x72 }; [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "The instances are disposed correctly.")] public static string ProtectData(string providerName, string providerUserId) { using (MemoryStream ms = new MemoryStream()) using (BinaryWriter bw = new BinaryWriter(ms)) { bw.Write(providerName); bw.Write(providerUserId); bw.Flush(); byte[] serializedWithPadding = new byte[ms.Length + _padding.Length]; Buffer.BlockCopy(_padding, 0, serializedWithPadding, 0, _padding.Length); Buffer.BlockCopy(ms.GetBuffer(), 0, serializedWithPadding, _padding.Length, (int)ms.Length); #pragma warning disable 0618 // Encode is [Obsolete] in 4.5 return MachineKey.Encode(serializedWithPadding, MachineKeyProtection.All); #pragma warning restore 0618 } } [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "The instances are disposed correctly.")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "All exception are being caught on purpose.")] public static bool UnprotectData(string protectedData, out string providerName, out string providerUserId) { providerName = null; providerUserId = null; if (String.IsNullOrEmpty(protectedData)) { return false; } #pragma warning disable 0618 // Decode is [Obsolete] in 4.5 byte[] decodedWithPadding = MachineKey.Decode(protectedData, MachineKeyProtection.All); #pragma warning restore 0618 if (decodedWithPadding.Length < _padding.Length) { return false; } // timing attacks aren't really applicable to this, so we just do the simple check. for (int i = 0; i < _padding.Length; i++) { if (_padding[i] != decodedWithPadding[i]) { return false; } } using (MemoryStream ms = new MemoryStream(decodedWithPadding, _padding.Length, decodedWithPadding.Length - _padding.Length)) using (BinaryReader br = new BinaryReader(ms)) { try { // use temp variable to keep both out parameters consistent and only set them when the input stream is read completely string name = br.ReadString(); string userId = br.ReadString(); // make sure that we consume the entire input stream if (ms.ReadByte() == -1) { providerName = name; providerUserId = userId; return true; } } catch { // Any exceptions will result in this method returning false. } } return false; } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Resources/OAuthResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.488 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.Web.WebPages.OAuth.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class OAuthResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal OAuthResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Web.WebPages.OAuth.Resources.OAuthResources", typeof(OAuthResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".. /// internal static string Security_NoExtendedMembershipProvider { get { return ResourceManager.GetString("Security_NoExtendedMembershipProvider", resourceCulture); } } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/Resources/OAuthResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider". ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/WebPagesOAuthDataProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Security; using DotNetOpenAuth.AspNet; using Microsoft.Web.WebPages.OAuth.Resources; using WebMatrix.WebData; namespace Microsoft.Web.WebPages.OAuth { internal class WebPagesOAuthDataProvider : IOpenAuthDataProvider { private static ExtendedMembershipProvider VerifyProvider() { var provider = Membership.Provider as ExtendedMembershipProvider; if (provider == null) { throw new InvalidOperationException(OAuthResources.Security_NoExtendedMembershipProvider); } return provider; } public string GetUserNameFromOpenAuth(string openAuthProvider, string openAuthId) { ExtendedMembershipProvider provider = VerifyProvider(); int userId = provider.GetUserIdFromOAuth(openAuthProvider, openAuthId); if (userId == -1) { return null; } return provider.GetUserNameFromId(userId); } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/WebPagesOAuthTokenManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Security; using DotNetOpenAuth.AspNet.Clients; using Microsoft.Internal.Web.Utils; using Microsoft.Web.WebPages.OAuth.Resources; using WebMatrix.WebData; namespace Microsoft.Web.WebPages.OAuth { /// /// WebPages implementation for the interface which store tokens into SimpleMembership database /// internal class WebPagesOAuthTokenManager : IOAuthTokenManager { private static ExtendedMembershipProvider VerifyProvider() { var provider = Membership.Provider as ExtendedMembershipProvider; if (provider == null) { throw new InvalidOperationException(OAuthResources.Security_NoExtendedMembershipProvider); } return provider; } /// /// Gets the token secret from the specified token. /// /// The token. /// /// The token's secret /// public string GetTokenSecret(string token) { if (String.IsNullOrEmpty(token)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "token"); } return VerifyProvider().GetOAuthTokenSecret(token); } /// /// Replaces the request token with access token. /// /// The request token. /// The access token. /// The access token secret. public void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) { if (String.IsNullOrEmpty(requestToken)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "requestToken"); } if (String.IsNullOrEmpty(accessToken)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "accessToken"); } if (String.IsNullOrEmpty(accessTokenSecret)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "accessTokenSecret"); } VerifyProvider().ReplaceOAuthRequestTokenWithAccessToken(requestToken, accessToken, accessTokenSecret); } /// /// Stores the request token together with its secret. /// /// The request token. /// The request token secret. public void StoreRequestToken(string requestToken, string requestTokenSecret) { if (String.IsNullOrEmpty(requestToken)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "requestToken"); } VerifyProvider().StoreOAuthRequestToken(requestToken, requestTokenSecret); } } } ================================================ FILE: src/Microsoft.Web.WebPages.OAuth/packages.config ================================================  ================================================ FILE: src/Settings.StyleCop ================================================ Parent ================================================ FILE: src/Strict.ruleset ================================================  ..\packages\CustomFxCopRules ================================================ FILE: src/System.Net.Http.Formatting/ByteRangeStreamContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Net.Http.Internal; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. The supports one or more /// byte ranges regardless of whether the ranges are consecutive or not. If there is only one range then a /// single partial response body containing a Content-Range header is generated. If there are more than one /// ranges then a multipart/byteranges response is generated where each body part contains a range indicated /// by the associated Content-Range header field. /// public class ByteRangeStreamContent : HttpContent { private const string SupportedRangeUnit = "bytes"; private const string ByteRangesContentSubtype = "byteranges"; private const int DefaultBufferSize = 4096; private const int MinBufferSize = 1; private readonly Stream _content; private readonly long _start; private readonly HttpContent _byteRangeContent; private bool _disposed; /// /// implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the parameter then an /// is thrown indicating the valid Content-Range of the content. /// /// The stream over which to generate a byte range view. /// The range or ranges, typically obtained from the Range HTTP request header field. /// The media type of the content stream. public ByteRangeStreamContent(Stream content, RangeHeaderValue range, string mediaType) : this(content, range, new MediaTypeHeaderValue(mediaType), DefaultBufferSize) { } /// /// implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the parameter then an /// is thrown indicating the valid Content-Range of the content. /// /// The stream over which to generate a byte range view. /// The range or ranges, typically obtained from the Range HTTP request header field. /// The media type of the content stream. /// The buffer size used when copying the content stream. public ByteRangeStreamContent(Stream content, RangeHeaderValue range, string mediaType, int bufferSize) : this(content, range, new MediaTypeHeaderValue(mediaType), bufferSize) { } /// /// implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the parameter then an /// is thrown indicating the valid Content-Range of the content. /// /// The stream over which to generate a byte range view. /// The range or ranges, typically obtained from the Range HTTP request header field. /// The media type of the content stream. public ByteRangeStreamContent(Stream content, RangeHeaderValue range, MediaTypeHeaderValue mediaType) : this(content, range, mediaType, DefaultBufferSize) { } /// /// implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the parameter then an /// is thrown indicating the valid Content-Range of the content. /// /// The stream over which to generate a byte range view. /// The range or ranges, typically obtained from the Range HTTP request header field. /// The media type of the content stream. /// The buffer size used when copying the content stream. public ByteRangeStreamContent(Stream content, RangeHeaderValue range, MediaTypeHeaderValue mediaType, int bufferSize) { if (content == null) { throw Error.ArgumentNull("content"); } if (!content.CanSeek) { throw Error.Argument("content", Properties.Resources.ByteRangeStreamNotSeekable, typeof(ByteRangeStreamContent).Name); } if (range == null) { throw Error.ArgumentNull("range"); } if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } if (!range.Unit.Equals(SupportedRangeUnit, StringComparison.OrdinalIgnoreCase)) { throw Error.Argument("range", Properties.Resources.ByteRangeStreamContentNotBytesRange, range.Unit, SupportedRangeUnit); } try { // If we have more than one range then we use a multipart/byteranges content type as wrapper. // Otherwise we use a non-multipart response. if (range.Ranges.Count > 1) { // Create Multipart content and copy headers to this content MultipartContent rangeContent = new MultipartContent(ByteRangesContentSubtype); _byteRangeContent = rangeContent; foreach (RangeItemHeaderValue rangeValue in range.Ranges) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, rangeValue); HttpContent rangeBodyPart = new StreamContent(rangeStream, bufferSize); rangeBodyPart.Headers.ContentType = mediaType; rangeBodyPart.Headers.ContentRange = rangeStream.ContentRange; rangeContent.Add(rangeBodyPart); } catch (ArgumentOutOfRangeException) { // We ignore range errors until we check that we have at least one valid range } } // If no overlapping ranges were found then stop if (!rangeContent.Any()) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = Error.Format(Properties.Resources.ByteRangeStreamNoneOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else if (range.Ranges.Count == 1) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, range.Ranges.First()); _byteRangeContent = new StreamContent(rangeStream, bufferSize); _byteRangeContent.Headers.ContentType = mediaType; _byteRangeContent.Headers.ContentRange = rangeStream.ContentRange; } catch (ArgumentOutOfRangeException) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = Error.Format(Properties.Resources.ByteRangeStreamNoOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else { throw Error.Argument("range", Properties.Resources.ByteRangeStreamContentNoRanges); } // Copy headers from byte range content so that we get the right content type etc. _byteRangeContent.Headers.CopyTo(Headers); _content = content; _start = content.Position; } catch { if (_byteRangeContent != null) { _byteRangeContent.Dispose(); } throw; } } protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { // Reset stream to start position _content.Position = _start; // Copy result to output return _byteRangeContent.CopyToAsync(stream); } protected override bool TryComputeLength(out long length) { long? contentLength = _byteRangeContent.Headers.ContentLength; if (contentLength.HasValue) { length = contentLength.Value; return true; } length = -1; return false; } protected override void Dispose(bool disposing) { Contract.Assert(_byteRangeContent != null); if (disposing) { if (!_disposed) { _byteRangeContent.Dispose(); _content.Dispose(); _disposed = true; } } base.Dispose(disposing); } } } ================================================ FILE: src/System.Net.Http.Formatting/CloneableExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http { internal static class CloneableExtensions { /// /// Convenience method for cloning objects that implement explicitly. /// /// The type of the cloneable object. /// The cloneable object. /// The result of cloning the . internal static T Clone(this T value) where T : ICloneable { return (T)value.Clone(); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/BaseJsonMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace System.Net.Http.Formatting { /// /// Abstract class to support Bson and Json. /// public abstract class BaseJsonMediaTypeFormatter : MediaTypeFormatter { // Though MaxDepth is not supported in netstandard1.3, we still override JsonReader's MaxDepth private int _maxDepth = FormattingUtilities.DefaultMaxDepth; private readonly IContractResolver _defaultContractResolver; private JsonSerializerSettings _jsonSerializerSettings; /// /// Initializes a new instance of the class. /// protected BaseJsonMediaTypeFormatter() { // Initialize serializer settings _defaultContractResolver = new JsonContractResolver(this); _jsonSerializerSettings = CreateDefaultSerializerSettings(); // Set default supported character encodings SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); SupportedEncodings.Add(new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true)); } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "MaxDepth is sealed in existing subclasses and its documentation carries warnings.")] protected BaseJsonMediaTypeFormatter(BaseJsonMediaTypeFormatter formatter) : base(formatter) { Contract.Assert(formatter != null); SerializerSettings = formatter.SerializerSettings; MaxDepth = formatter._maxDepth; } /// /// Gets or sets the used to configure the . /// public JsonSerializerSettings SerializerSettings { get { return _jsonSerializerSettings; } set { if (value == null) { throw Error.ArgumentNull("value"); } _jsonSerializerSettings = value; } } /// /// Gets or sets the maximum depth allowed by this formatter. /// /// /// Any override must call the base getter and setter. The setter may be called before a derived class /// constructor runs, so any override should be very careful about using derived class state. /// public virtual int MaxDepth { get { return _maxDepth; } set { if (value < FormattingUtilities.DefaultMinDepth) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, FormattingUtilities.DefaultMinDepth); } _maxDepth = value; } } /// /// Creates a instance with the default settings used by the . /// public JsonSerializerSettings CreateDefaultSerializerSettings() { return new JsonSerializerSettings() { ContractResolver = _defaultContractResolver, MissingMemberHandling = MissingMemberHandling.Ignore, // Do not change this setting // Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types TypeNameHandling = TypeNameHandling.None }; } /// /// Determines whether this can read objects /// of the specified . /// /// The of object that will be read. /// true if objects of this can be read, otherwise false. public override bool CanReadType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } return true; } /// /// Determines whether this can write objects /// of the specified . /// /// The of object that will be written. /// true if objects of this can be written, otherwise false. public override bool CanWriteType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } return true; } /// /// Called during deserialization to read an object of the specified /// from the specified . /// /// The of object to read. /// The from which to read. /// The for the content being written. /// The to log events to. /// A whose result will be the object instance that has been read. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } try { return Task.FromResult(ReadFromStream(type, readStream, content, formatterLogger)); } catch (Exception e) { return TaskHelpers.FromError(e); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Caller's formatterLogger is notified of problem in all cases where Exception is not rethrown.")] private object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { Contract.Assert(type != null); Contract.Assert(readStream != null); HttpContentHeaders contentHeaders = content == null ? null : content.Headers; // If content length is 0 then return default value for this type if (contentHeaders != null && contentHeaders.ContentLength == 0) { return GetDefaultValueForType(type); } // Get the character encoding for the content // Never non-null since SelectCharacterEncoding() throws in error / not found scenarios Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders); try { return ReadFromStream(type, readStream, effectiveEncoding, formatterLogger); } catch (Exception e) { if (formatterLogger == null) { throw; } formatterLogger.LogError(String.Empty, e); return GetDefaultValueForType(type); } } /// /// Called during deserialization to read an object of the specified /// from the specified . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// . /// /// The of object to read. /// The from which to read. /// The to use when reading. /// The to log events to. /// The instance that has been read. public virtual object ReadFromStream(Type type, Stream readStream, Encoding effectiveEncoding, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } using (JsonReader jsonReader = CreateJsonReaderInternal(type, readStream, effectiveEncoding)) { jsonReader.CloseInput = false; jsonReader.MaxDepth = _maxDepth; JsonSerializer jsonSerializer = CreateJsonSerializerInternal(); EventHandler errorHandler = null; if (formatterLogger != null) { // Error must always be marked as handled // Failure to do so can cause the exception to be rethrown at every recursive level and overflow the stack for x64 CLR processes errorHandler = (sender, e) => { Exception exception = e.ErrorContext.Error; formatterLogger.LogError(e.ErrorContext.Path, exception); e.ErrorContext.Handled = true; }; jsonSerializer.Error += errorHandler; } try { return jsonSerializer.Deserialize(jsonReader, type); } finally { if (errorHandler != null) { // Clean up the error handler in case CreateJsonSerializer() reuses a serializer jsonSerializer.Error -= errorHandler; } } } } private JsonReader CreateJsonReaderInternal(Type type, Stream readStream, Encoding effectiveEncoding) { Contract.Assert(type != null); Contract.Assert(readStream != null); Contract.Assert(effectiveEncoding != null); JsonReader reader = CreateJsonReader(type, readStream, effectiveEncoding); if (reader == null) { throw Error.InvalidOperation(Properties.Resources.MediaTypeFormatter_JsonReaderFactoryReturnedNull, "CreateJsonReader"); } return reader; } /// /// Called during deserialization to get the . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// . /// /// The of object to read. /// The from which to read. /// The to use when reading. /// The used during deserialization. public abstract JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding); [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is a public extensibility point, we can't predict what exceptions will come through")] private JsonSerializer CreateJsonSerializerInternal() { JsonSerializer serializer = null; try { serializer = CreateJsonSerializer(); } catch (Exception exception) { throw Error.InvalidOperation(exception, Properties.Resources.JsonSerializerFactoryThrew, "CreateJsonSerializer"); } if (serializer == null) { throw Error.InvalidOperation(Properties.Resources.JsonSerializerFactoryReturnedNull, "CreateJsonSerializer"); } return serializer; } /// /// Called during serialization and deserialization to get the . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// and . /// /// The used during serialization and deserialization. public virtual JsonSerializer CreateJsonSerializer() { JsonSerializer jsonSerializer = JsonSerializer.Create(SerializerSettings); return jsonSerializer; } /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (cancellationToken.IsCancellationRequested) { return TaskHelpers.Canceled(); } try { WriteToStream(type, value, writeStream, content); return TaskHelpers.Completed(); } catch (Exception e) { return TaskHelpers.FromError(e); } } private void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { Contract.Assert(type != null); Contract.Assert(writeStream != null); Encoding effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers); WriteToStream(type, value, writeStream, effectiveEncoding); } /// /// Called during serialization to write an object of the specified /// to the specified . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// . /// /// The of object to write. /// The object to write. /// The to which to write. /// The to use when writing. public virtual void WriteToStream(Type type, object value, Stream writeStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } using (JsonWriter jsonWriter = CreateJsonWriterInternal(type, writeStream, effectiveEncoding)) { jsonWriter.CloseOutput = false; JsonSerializer jsonSerializer = CreateJsonSerializerInternal(); jsonSerializer.Serialize(jsonWriter, value); jsonWriter.Flush(); } } private JsonWriter CreateJsonWriterInternal(Type type, Stream writeStream, Encoding effectiveEncoding) { Contract.Assert(type != null); Contract.Assert(writeStream != null); Contract.Assert(effectiveEncoding != null); JsonWriter writer = CreateJsonWriter(type, writeStream, effectiveEncoding); if (writer == null) { throw Error.InvalidOperation(Properties.Resources.MediaTypeFormatter_JsonWriterFactoryReturnedNull, "CreateJsonWriter"); } return writer; } /// /// Called during serialization to get the . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// . /// /// The of object to write. /// The to which to write. /// The to use when writing. /// The used during serialization. public abstract JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding); } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/BsonMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Bson; namespace System.Net.Http.Formatting { /// /// class to handle Bson. /// public class BsonMediaTypeFormatter : BaseJsonMediaTypeFormatter { private static readonly Type OpenDictionaryType = typeof(Dictionary<,>); /// /// Initializes a new instance of the class. /// public BsonMediaTypeFormatter() { // Set default supported media type SupportedMediaTypes.Add(MediaTypeConstants.ApplicationBsonMediaType); } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected BsonMediaTypeFormatter(BsonMediaTypeFormatter formatter) : base(formatter) { } /// /// Gets the default media type for Json, namely "application/bson". /// /// /// The default media type does not have any charset parameter as /// the can be configured on a per /// instance basis. /// /// /// Because is mutable, the value /// returned will be a new instance every time. /// public static MediaTypeHeaderValue DefaultMediaType { get { return MediaTypeConstants.ApplicationBsonMediaType; } } /// public sealed override int MaxDepth { get { return base.MaxDepth; } set { base.MaxDepth = value; } } /// public override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (type == typeof(DBNull) && content != null && content.Headers != null && content.Headers.ContentLength == 0) { // Lower-level Json.Net deserialization can convert null to DBNull.Value. However this formatter treats // DBNull.Value like null and serializes no content. Json.Net code won't be invoked at all (for read or // write). Override BaseJsonMediaTypeFormatter.ReadFromStream()'s call to GetDefaultValueForType() // (which would return null in this case) and instead return expected DBNull.Value. Special case exists // primarily for parity with JsonMediaTypeFormatter. return Task.FromResult((object)DBNull.Value); } else { return base.ReadFromStreamAsync(type, readStream, content, formatterLogger); } } /// public override object ReadFromStream(Type type, Stream readStream, Encoding effectiveEncoding, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } // Special-case for simple types: Deserialize a Dictionary with a single element named Value. // Serialization created this Dictionary to work around BSON restrictions: BSON cannot // handle a top-level simple type. NewtonSoft.Json throws a JsonWriterException with message "Error // writing Binary value. BSON must start with an Object or Array. Path ''." when WriteToStream() is given // such a value. // // Added clause for typeof(byte[]) needed because NewtonSoft.Json sometimes throws above Exception when // WriteToStream() is given a byte[] value. (Not clear where the bug lies and, worse, it doesn't reproduce // reliably.) // // Request for typeof(object) may cause a simple value to round trip as a JObject. if (IsSimpleType(type) || type == typeof(byte[])) { // Read as exact expected Dictionary to ensure NewtonSoft.Json does correct top-level conversion. Type dictionaryType = OpenDictionaryType.MakeGenericType(new Type[] { typeof(string), type }); IDictionary dictionary = base.ReadFromStream(dictionaryType, readStream, effectiveEncoding, formatterLogger) as IDictionary; if (dictionary == null) { // Not valid since BaseJsonMediaTypeFormatter.ReadFromStream(Type, Stream, HttpContent, IFormatterLogger) // handles empty content and does not call ReadFromStream(Type, Stream, Encoding, IFormatterLogger) // in that case. throw Error.InvalidOperation(Properties.Resources.MediaTypeFormatter_BsonParseError_MissingData, dictionaryType.Name); } // Unfortunately IDictionary doesn't have TryGetValue()... string firstKey = String.Empty; foreach (DictionaryEntry item in dictionary) { if (dictionary.Count == 1 && (item.Key as string) == "Value") { // Success return item.Value; } else { if (item.Key != null) { firstKey = item.Key.ToString(); } break; } } throw Error.InvalidOperation(Properties.Resources.MediaTypeFormatter_BsonParseError_UnexpectedData, dictionary.Count, firstKey); } else { return base.ReadFromStream(type, readStream, effectiveEncoding, formatterLogger); } } /// public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } BsonDataReader reader = new BsonDataReader(new BinaryReader(readStream, effectiveEncoding)); try { // Special case discussed at http://stackoverflow.com/questions/16910369/bson-array-deserialization-with-json-net // Dispensed with string (aka IEnumerable) case above in ReadFromStream() reader.ReadRootValueAsArray = typeof(IEnumerable).IsAssignableFrom(type) && !typeof(IDictionary).IsAssignableFrom(type); } catch { // Ensure instance is cleaned up in case of an issue ((IDisposable)reader).Dispose(); throw; } return reader; } /// public override void WriteToStream(Type type, object value, Stream writeStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } if (value == null) { // Cannot serialize null at the top level. Json.Net throws Newtonsoft.Json.JsonWriterException : Error // writing Null value. BSON must start with an Object or Array. Path ''. Fortunately // BaseJsonMediaTypeFormatter.ReadFromStream(Type, Stream, HttpContent, IFormatterLogger) treats zero- // length content as null or the default value of a struct. return; } if (value == DBNull.Value) { // ReadFromStreamAsync() override above converts null to DBNull.Value if given Type is DBNull; normally // however DBNull.Value round-trips as null. There's a known edge case where a full .NET application // uses the portable version of this formatter. The full .NET application may pass DBNull.Value, // leading to a JsonWriterException. If a DBNull is at a lower level in the value, the portable // Json.Net assembly will also fail to special-case that DBNull, serialize it as an empty JSON object // rather than null, and not meet the receiver's expectations. return; } // See comments in ReadFromStream() above about this special case and the need to include byte[] in it. // Using runtime type here because Json.Net will throw during serialization whenever it cannot handle the // runtime type at the top level. For e.g. passed type may be typeof(object) and value may be a string. Type runtimeType = value.GetType(); if (IsSimpleType(runtimeType) || runtimeType == typeof(byte[])) { // Wrap value in a Dictionary with a single property named "Value" to provide BSON with an Object. Is // written out as binary equivalent of { "Value": value } JSON. Dictionary temporaryDictionary = new Dictionary { { "Value", value }, }; base.WriteToStream(typeof(Dictionary), temporaryDictionary, writeStream, effectiveEncoding); } else { base.WriteToStream(type, value, writeStream, effectiveEncoding); } } /// public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } return new BsonDataWriter(new BinaryWriter(writeStream, effectiveEncoding)); } // Return true if Json.Net will likely convert value of given type to a Json primitive, not JsonArray nor // JsonObject. // To do: https://aspnetwebstack.codeplex.com/workitem/1467 private static bool IsSimpleType(Type type) { Contract.Assert(type != null); // CanConvertFrom() check is similar to MVC / Web API ModelMetadata.IsComplexType getters. This is // sufficient for many cases but Json.Net uses JsonConverterAttribute and built-in converters, not type // descriptors. bool isSimpleType = TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string)); return isSimpleType; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/BufferedMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Net.Http.Internal; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Base class for writing a synchronous formatter on top of the asynchronous formatter infrastructure. /// This does not guarantee non-blocking threads. The only way to guarantee that we don't block a thread on IO is /// to use the asynchronous . /// public abstract class BufferedMediaTypeFormatter : MediaTypeFormatter { private const int MinBufferSize = 0; private const int DefaultBufferSize = 16 * 1024; private int _bufferSizeInBytes = DefaultBufferSize; /// /// Initializes a new instance of the class. /// protected BufferedMediaTypeFormatter() { } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected BufferedMediaTypeFormatter(BufferedMediaTypeFormatter formatter) : base(formatter) { BufferSize = formatter.BufferSize; } /// /// Suggested size of buffer to use with streams, in bytes. The default size is 16K. /// public int BufferSize { get { return _bufferSizeInBytes; } set { if (value < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, MinBufferSize); } _bufferSizeInBytes = value; } } /// /// Writes synchronously to the buffered stream. /// /// /// An implementation of this method should close upon completion. /// /// The type of the object to write. /// The object value to write. It may be null. /// The to which to write. /// The if available. Note that /// modifying the headers of the content will have no effect on the generated HTTP message; they should only be used to guide the writing. /// The token to monitor for cancellation requests. public virtual void WriteToStream(Type type, object value, Stream writeStream, HttpContent content, CancellationToken cancellationToken) { WriteToStream(type, value, writeStream, content); } /// /// Writes synchronously to the buffered stream. /// /// /// An implementation of this method should close upon completion. /// /// The type of the object to write. /// The object value to write. It may be null. /// The to which to write. /// The if available. Note that /// modifying the headers of the content will have no effect on the generated HTTP message; they should only be used to guide the writing. public virtual void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { throw Error.NotSupported(Properties.Resources.MediaTypeFormatterCannotWriteSync, GetType().Name); } /// /// Reads synchronously from the buffered stream. /// /// /// An implementation of this method should close upon completion. /// /// The type of the object to deserialize. /// The to read. /// The if available. /// The to log events to. /// The token to monitor for cancellation requests. /// An object of the given type. public virtual object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { return ReadFromStream(type, readStream, content, formatterLogger); } /// /// Reads synchronously from the buffered stream. /// /// /// An implementation of this method should close upon completion. /// /// The type of the object to deserialize. /// The to read. /// The if available. /// The to log events to. /// An object of the given type. public virtual object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { throw Error.NotSupported(Properties.Resources.MediaTypeFormatterCannotReadSync, GetType().Name); } // Sealed because derived classes shouldn't override the async version. Override sync version instead. public sealed override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) { return WriteToStreamAsync(type, value, writeStream, content, transportContext, CancellationToken.None); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public sealed override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } try { WriteToStreamSync(type, value, writeStream, content, cancellationToken); return TaskHelpers.Completed(); } catch (Exception e) { return TaskHelpers.FromError(e); } } private void WriteToStreamSync(Type type, object value, Stream writeStream, HttpContent content, CancellationToken cancellationToken) { using (Stream bufferedStream = GetBufferStream(writeStream, _bufferSizeInBytes)) { WriteToStream(type, value, bufferedStream, content, cancellationToken); } } public sealed override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { return ReadFromStreamAsync(type, readStream, content, formatterLogger, CancellationToken.None); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public sealed override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } try { return Task.FromResult(ReadFromStreamSync(type, readStream, content, formatterLogger, cancellationToken)); } catch (Exception e) { return TaskHelpers.FromError(e); } } private object ReadFromStreamSync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { object result; HttpContentHeaders contentHeaders = content == null ? null : content.Headers; if (contentHeaders != null && contentHeaders.ContentLength == 0) { result = GetDefaultValueForType(type); } else { using (Stream bufferedStream = GetBufferStream(readStream, _bufferSizeInBytes)) { result = ReadFromStream(type, bufferedStream, content, formatterLogger, cancellationToken); } } return result; } private static Stream GetBufferStream(Stream innerStream, int bufferSize) { Contract.Assert(innerStream != null); // We wrap the inner stream in a non-closing delegating stream so that we allow the user // to use the using (...) pattern yet not break the contract of formatters not closing // the inner stream. Stream nonClosingStream = new NonClosingDelegatingStream(innerStream); // This uses a naive buffering. BufferedStream() will block the thread while it drains the buffer. // We can explore a smarter implementation that async drains the buffer. return new BufferedStream(nonClosingStream, bufferSize); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/ContentNegotiationResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Represents the result of content negotiation performed using /// /// public class ContentNegotiationResult { private MediaTypeFormatter _formatter; /// /// Create the content negotiation result object. /// /// The formatter. /// The preferred media type. Can be null. public ContentNegotiationResult(MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } _formatter = formatter; MediaType = mediaType; } /// /// The formatter chosen for serialization. /// public MediaTypeFormatter Formatter { get { return _formatter; } set { if (value == null) { throw Error.ArgumentNull("value"); } _formatter = value; } } /// /// The media type that is associated with the formatter chosen for serialization. Can be null. /// public MediaTypeHeaderValue MediaType { get; set; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/DefaultContentNegotiator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Linq; using System.Net.Http.Headers; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Class that selects a for an /// or . /// public class DefaultContentNegotiator : IContentNegotiator { public DefaultContentNegotiator() : this(false) { } /// /// Initializes a new instance of the with /// the given setting for . /// /// /// If ExcludeMatchOnTypeOnly is true then we don't match on type only which means /// that we return null if we can't match on anything in the request. This is useful /// for generating 406 (Not Acceptable) status codes. /// public DefaultContentNegotiator(bool excludeMatchOnTypeOnly) { ExcludeMatchOnTypeOnly = excludeMatchOnTypeOnly; } /// /// If ExcludeMatchOnTypeOnly is true then we don't match on type only which means /// that we return null if we can't match on anything in the request. This is useful /// for generating 406 (Not Acceptable) status codes. /// public bool ExcludeMatchOnTypeOnly { get; private set; } /// /// Performs content negotiating by selecting the most appropriate out of the passed in /// for the given that can serialize an object of the given /// . /// /// The type to be serialized. /// The request. /// The set of objects from which to choose. /// The result of the negotiation containing the most appropriate instance, /// or null if there is no appropriate formatter. public virtual ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable formatters) { // Performance-sensitive if (type == null) { throw Error.ArgumentNull("type"); } if (request == null) { throw Error.ArgumentNull("request"); } if (formatters == null) { throw Error.ArgumentNull("formatters"); } // Go through each formatter to compute how well it matches. Collection matches = ComputeFormatterMatches(type, request, formatters); // Select best formatter match among the matches MediaTypeFormatterMatch bestFormatterMatch = SelectResponseMediaTypeFormatter(matches); // We found a best formatter if (bestFormatterMatch != null) { // Find the best character encoding for the selected formatter Encoding bestEncodingMatch = SelectResponseCharacterEncoding(request, bestFormatterMatch.Formatter); if (bestEncodingMatch != null) { bestFormatterMatch.MediaType.CharSet = bestEncodingMatch.WebName; } MediaTypeHeaderValue bestMediaType = bestFormatterMatch.MediaType; MediaTypeFormatter bestFormatter = bestFormatterMatch.Formatter.GetPerRequestFormatterInstance(type, request, bestMediaType); return new ContentNegotiationResult(bestFormatter, bestMediaType); } return null; } /// /// Determine how well each formatter matches by associating a value /// with the formatter. Then associate the quality of the match based on q-factors and other parameters. The result of this /// method is a collection of the matches found categorized and assigned a quality value. /// /// The type to be serialized. /// The request. /// The set of objects from which to choose. /// A collection containing all the matches. protected virtual Collection ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable formatters) { // Performance-sensitive if (type == null) { throw Error.ArgumentNull("type"); } if (request == null) { throw Error.ArgumentNull("request"); } if (formatters == null) { throw Error.ArgumentNull("formatters"); } IEnumerable sortedAcceptValues = null; // Go through each formatter to find how well it matches. ListWrapperCollection matches = new ListWrapperCollection(); MediaTypeFormatter[] writingFormatters = GetWritingFormatters(formatters); for (int i = 0; i < writingFormatters.Length; i++) { MediaTypeFormatter formatter = writingFormatters[i]; MediaTypeFormatterMatch match = null; // Check first that formatter can write the actual type if (!formatter.CanWriteType(type)) { // Formatter can't even write the type so no match at all continue; } // Match against media type mapping. if ((match = MatchMediaTypeMapping(request, formatter)) != null) { matches.Add(match); continue; } // Match against the accept header values. if (sortedAcceptValues == null) { // Sort the Accept header values in descending order based on q-factor sortedAcceptValues = SortMediaTypeWithQualityHeaderValuesByQFactor(request.Headers.Accept); } if ((match = MatchAcceptHeader(sortedAcceptValues, formatter)) != null) { matches.Add(match); continue; } // Match against request's media type if any if ((match = MatchRequestMediaType(request, formatter)) != null) { matches.Add(match); continue; } // Check whether we should match on type or stop the matching process. // The latter is used to generate 406 (Not Acceptable) status codes. bool shouldMatchOnType = ShouldMatchOnType(sortedAcceptValues); // Match against the type of object we are writing out if (shouldMatchOnType && (match = MatchType(type, formatter)) != null) { matches.Add(match); continue; } } return matches; } /// /// Select the best match among the candidate matches found. /// /// The collection of matches. /// The determined to be the best match. protected virtual MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection matches) { // Performance-sensitive if (matches == null) { throw Error.ArgumentNull("matches"); } List matchList = matches.AsList(); MediaTypeFormatterMatch bestMatchOnType = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderLiteral = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderSubtypeMediaRange = null; MediaTypeFormatterMatch bestMatchOnAcceptHeaderAllMediaRange = null; MediaTypeFormatterMatch bestMatchOnMediaTypeMapping = null; MediaTypeFormatterMatch bestMatchOnRequestMediaType = null; // Go through each formatter to find the best match in each category. for (int i = 0; i < matchList.Count; i++) { MediaTypeFormatterMatch match = matchList[i]; switch (match.Ranking) { case MediaTypeFormatterMatchRanking.MatchOnCanWriteType: // First match by type trumps all other type matches if (bestMatchOnType == null) { bestMatchOnType = match; } break; case MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping: // Matches on accept headers using mappings must choose the highest quality match bestMatchOnMediaTypeMapping = UpdateBestMatch(bestMatchOnMediaTypeMapping, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderLiteral = UpdateBestMatch(bestMatchOnAcceptHeaderLiteral, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderSubtypeMediaRange = UpdateBestMatch(bestMatchOnAcceptHeaderSubtypeMediaRange, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange: // Matches on accept headers must choose the highest quality match. // A match of 0.0 means we won't use it at all. bestMatchOnAcceptHeaderAllMediaRange = UpdateBestMatch(bestMatchOnAcceptHeaderAllMediaRange, match); break; case MediaTypeFormatterMatchRanking.MatchOnRequestMediaType: // First match on request content type trumps other request content matches if (bestMatchOnRequestMediaType == null) { bestMatchOnRequestMediaType = match; } break; } } // If we received matches based on both supported media types and from media type mappings, // we want to give precedence to the media type mappings, but only if their quality is >= that of the supported media type. // We do this because media type mappings are the user's extensibility point and must take precedence over normal // supported media types in the case of a tie. The 99% case is where both have quality 1.0. if (bestMatchOnMediaTypeMapping != null) { MediaTypeFormatterMatch mappingOverride = bestMatchOnMediaTypeMapping; mappingOverride = UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderLiteral); mappingOverride = UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderSubtypeMediaRange); mappingOverride = UpdateBestMatch(mappingOverride, bestMatchOnAcceptHeaderAllMediaRange); if (mappingOverride != bestMatchOnMediaTypeMapping) { bestMatchOnMediaTypeMapping = null; } } // now select the formatter and media type // A MediaTypeMapping is highest precedence -- it is an extensibility point // allowing the user to override normal accept header matching MediaTypeFormatterMatch bestMatch = null; if (bestMatchOnMediaTypeMapping != null) { bestMatch = bestMatchOnMediaTypeMapping; } else if (bestMatchOnAcceptHeaderLiteral != null || bestMatchOnAcceptHeaderSubtypeMediaRange != null || bestMatchOnAcceptHeaderAllMediaRange != null) { bestMatch = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderLiteral); bestMatch = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderSubtypeMediaRange); bestMatch = UpdateBestMatch(bestMatch, bestMatchOnAcceptHeaderAllMediaRange); } else if (bestMatchOnRequestMediaType != null) { bestMatch = bestMatchOnRequestMediaType; } else if (bestMatchOnType != null) { bestMatch = bestMatchOnType; } return bestMatch; } /// /// Determine the best character encoding for writing the response. First we look /// for accept-charset headers and if not found then we try to match /// any charset encoding in the request (in case of PUT, POST, etc.) /// If no encoding is found then we use the default for the formatter. /// /// The determined to be the best match. protected virtual Encoding SelectResponseCharacterEncoding(HttpRequestMessage request, MediaTypeFormatter formatter) { if (request == null) { throw Error.ArgumentNull("request"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } // If there are any SupportedEncodings then we pick an encoding List supportedEncodings = formatter.SupportedEncodingsInternal; if (supportedEncodings.Count > 0) { // Sort Accept-Charset header values IEnumerable sortedAcceptCharsetValues = SortStringWithQualityHeaderValuesByQFactor(request.Headers.AcceptCharset); // Check for match based on accept-charset headers foreach (StringWithQualityHeaderValue acceptCharset in sortedAcceptCharsetValues) { for (int i = 0; i < supportedEncodings.Count; i++) { Encoding encoding = supportedEncodings[i]; if (encoding != null && acceptCharset.Quality != FormattingUtilities.NoMatch && (acceptCharset.Value.Equals(encoding.WebName, StringComparison.OrdinalIgnoreCase) || acceptCharset.Value.Equals("*", StringComparison.OrdinalIgnoreCase))) { return encoding; } } } // Check for match based on any request entity body return formatter.SelectCharacterEncoding(request.Content != null ? request.Content.Headers : null); } return null; } /// /// Match a request against the s registered with the formatter. /// /// The request to match. /// The formatter to match against. /// A indicating the quality of the match or null is no match. protected virtual MediaTypeFormatterMatch MatchMediaTypeMapping(HttpRequestMessage request, MediaTypeFormatter formatter) { if (request == null) { throw Error.ArgumentNull("request"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } List mediaTypeMappings = formatter.MediaTypeMappingsInternal; for (int i = 0; i < mediaTypeMappings.Count; i++) { MediaTypeMapping mapping = mediaTypeMappings[i]; double quality; if (mapping != null && ((quality = mapping.TryMatchMediaType(request)) > FormattingUtilities.NoMatch)) { return new MediaTypeFormatterMatch(formatter, mapping.MediaType, quality, MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping); } } return null; } /// /// Match the request accept header field values against the formatter's registered supported media types. /// /// The sorted accept header values to match. /// The formatter to match against. /// A indicating the quality of the match or null is no match. protected virtual MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable sortedAcceptValues, MediaTypeFormatter formatter) { if (sortedAcceptValues == null) { throw Error.ArgumentNull("sortedAcceptValues"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } foreach (MediaTypeWithQualityHeaderValue acceptMediaTypeValue in sortedAcceptValues) { List supportedMediaTypes = formatter.SupportedMediaTypesInternal; for (int i = 0; i < supportedMediaTypes.Count; i++) { MediaTypeHeaderValue supportedMediaType = supportedMediaTypes[i]; MediaTypeHeaderValueRange range; if (supportedMediaType != null && acceptMediaTypeValue.Quality != FormattingUtilities.NoMatch && supportedMediaType.IsSubsetOf(acceptMediaTypeValue, out range)) { MediaTypeFormatterMatchRanking ranking; switch (range) { case MediaTypeHeaderValueRange.AllMediaRange: ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange; break; case MediaTypeHeaderValueRange.SubtypeMediaRange: ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange; break; default: ranking = MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral; break; } return new MediaTypeFormatterMatch(formatter, supportedMediaType, acceptMediaTypeValue.Quality, ranking); } } } return null; } /// /// Match any request media type (in case there is a request entity body) against the formatter's registered /// media types. /// /// The request to match. /// The formatter to match against. /// A indicating the quality of the match or null is no match. protected virtual MediaTypeFormatterMatch MatchRequestMediaType(HttpRequestMessage request, MediaTypeFormatter formatter) { if (request == null) { throw Error.ArgumentNull("request"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } if (request.Content != null) { MediaTypeHeaderValue requestMediaType = request.Content.Headers.ContentType; if (requestMediaType != null) { List supportedMediaTypes = formatter.SupportedMediaTypesInternal; for (int i = 0; i < supportedMediaTypes.Count; i++) { MediaTypeHeaderValue supportedMediaType = supportedMediaTypes[i]; if (supportedMediaType != null && supportedMediaType.IsSubsetOf(requestMediaType)) { return new MediaTypeFormatterMatch(formatter, supportedMediaType, FormattingUtilities.Match, MediaTypeFormatterMatchRanking.MatchOnRequestMediaType); } } } } return null; } /// /// Determine whether to match on type or not. This is used to determine whether to /// generate a 406 response or use the default media type formatter in case there /// is no match against anything in the request. If ExcludeMatchOnTypeOnly is true /// then we don't match on type unless there are no accept headers. /// /// The sorted accept header values to match. /// True if not ExcludeMatchOnTypeOnly and accept headers with a q-factor bigger than 0.0 are present. protected virtual bool ShouldMatchOnType(IEnumerable sortedAcceptValues) { if (sortedAcceptValues == null) { throw Error.ArgumentNull("sortedAcceptValues"); } return !(ExcludeMatchOnTypeOnly && sortedAcceptValues.Any()); } /// /// Pick the first supported media type and indicate we've matched only on type /// /// The type to be serialized. /// The formatter we are matching against. /// A indicating the quality of the match or null is no match. protected virtual MediaTypeFormatterMatch MatchType(Type type, MediaTypeFormatter formatter) { // Performance-sensitive if (type == null) { throw Error.ArgumentNull("type"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } // We already know that we do match on type -- otherwise we wouldn't even be called -- // so this is just a matter of determining how we match. MediaTypeHeaderValue mediaType = null; List supportedMediaTypes = formatter.SupportedMediaTypesInternal; if (supportedMediaTypes.Count > 0) { mediaType = supportedMediaTypes[0]; } return new MediaTypeFormatterMatch(formatter, mediaType, FormattingUtilities.Match, MediaTypeFormatterMatchRanking.MatchOnCanWriteType); } /// /// Sort Accept header values and related header field values with similar syntax rules /// (if more than 1) in descending order based on q-factor. /// /// The header values to sort. /// The sorted header values. protected virtual IEnumerable SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection headerValues) { if (headerValues == null) { throw Error.ArgumentNull("headerValues"); } if (headerValues.Count > 1) { // Use OrderBy() instead of Array.Sort() as it performs fewer comparisons. In this case the comparisons // are quite expensive so OrderBy() performs better. return headerValues.OrderByDescending(m => m, MediaTypeWithQualityHeaderValueComparer.QualityComparer).ToArray(); } else { return headerValues; } } /// /// Sort Accept-Charset, Accept-Encoding, Accept-Language and related header field values with similar syntax rules /// (if more than 1) in descending order based on q-factor. /// /// The header values to sort. /// The sorted header values. protected virtual IEnumerable SortStringWithQualityHeaderValuesByQFactor(ICollection headerValues) { if (headerValues == null) { throw Error.ArgumentNull("headerValues"); } if (headerValues.Count > 1) { // Use OrderBy() instead of Array.Sort() as it performs fewer comparisons. In this case the comparisons // are quite expensive so OrderBy() performs better. return headerValues.OrderByDescending(m => m, StringWithQualityHeaderValueComparer.QualityComparer).ToArray(); } else { return headerValues; } } /// /// Evaluates whether a match is better than the current match and if so returns the replacement; otherwise returns the /// current match. /// protected virtual MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement) { if (potentialReplacement == null) { return current; } if (current != null) { return (potentialReplacement.Quality > current.Quality) ? potentialReplacement : current; } return potentialReplacement; } private static MediaTypeFormatter[] GetWritingFormatters(IEnumerable formatters) { Contract.Assert(formatters != null); MediaTypeFormatterCollection formatterCollection = formatters as MediaTypeFormatterCollection; if (formatterCollection != null) { return formatterCollection.WritingFormatters; } return formatters.AsArray(); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/DelegatingEnumerable.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Helper class to serialize types by delegating them through a concrete implementation."/>. /// /// The interface implementing to proxy. [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Enumerable conveys the meaning of collection")] public sealed class DelegatingEnumerable : IEnumerable { private IEnumerable _source; /// /// Initialize a DelegatingEnumerable. This constructor is necessary for to work. /// public DelegatingEnumerable() { _source = Enumerable.Empty(); } /// /// Initialize a DelegatingEnumerable with an . This is a helper class to proxy interfaces for . /// /// The instance to get the enumerator from. public DelegatingEnumerable(IEnumerable source) { if (source == null) { throw Error.ArgumentNull("source"); } _source = source; } /// /// Get the enumerator of the associated . /// /// The enumerator of the source. public IEnumerator GetEnumerator() { return _source.GetEnumerator(); } /// /// This method is not implemented but is required method for serialization to work. Do not use. /// /// The item to add. Unused. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "Required by XmlSerializer, never used.")] public void Add(object item) { throw new NotImplementedException(); } /// /// Get the enumerator of the associated . /// /// The enumerator of the source. IEnumerator IEnumerable.GetEnumerator() { return _source.GetEnumerator(); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/FormDataCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Formatting.Internal; using System.Net.Http.Formatting.Parsers; using System.Text; using System.Threading; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Represent the form data. /// - This has 100% fidelity (including ordering, which is important for deserializing ordered array). /// - using interfaces allows us to optimize the implementation. E.g., we can avoid eagerly string-splitting a 10gb file. /// - This also provides a convenient place to put extension methods. /// public class FormDataCollection : IEnumerable> { private readonly IEnumerable> _pairs; private NameValueCollection _nameValueCollection; /// /// Initialize a form collection around incoming data. /// The key value enumeration should be immutable. /// /// incoming set of key value pairs. Ordering is preserved. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is the convention for representing FormData")] public FormDataCollection(IEnumerable> pairs) { if (pairs == null) { throw Error.ArgumentNull("pairs"); } _pairs = pairs; } /// /// Initialize a form collection from a query string. /// Uri and FormURl body have the same schema. /// public FormDataCollection(Uri uri) { if (uri == null) { throw Error.ArgumentNull("uri"); } string query = uri.Query; if (query != null && query.Length > 0 && query[0] == '?') { query = query.Substring(1); } _pairs = ParseQueryString(query); } /// /// Initialize a form collection from a URL encoded query string. Any leading question /// mark (?) will be considered part of the query string and treated as any other value. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "string is a query string, not a URI")] public FormDataCollection(string query) { _pairs = ParseQueryString(query); } /// /// Gets values associated with a given key. If there are multiple values, they're concatenated. /// /// The name of the entry that contains the values to get. The name can be null. /// Values associated with a given key. If there are multiple values, they're concatenated. public string this[string name] { get { return Get(name); } } // Helper to invoke parser around a query string private static IEnumerable> ParseQueryString(string query) { List> result = new List>(); if (String.IsNullOrWhiteSpace(query)) { return result; } byte[] bytes = Encoding.UTF8.GetBytes(query); FormUrlEncodedParser parser = new FormUrlEncodedParser(result, Int64.MaxValue); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(bytes, bytes.Length, ref bytesConsumed, isFinal: true); if (state != ParserState.Done) { throw Error.InvalidOperation(Properties.Resources.FormUrlEncodedParseError, bytesConsumed); } return result; } /// /// Get the collection as a NameValueCollection. /// Beware this loses some ordering. Values are ordered within a key, /// but keys are no longer ordered against each other. /// public NameValueCollection ReadAsNameValueCollection() { if (_nameValueCollection == null) { // Initialize in a private collection to be thread-safe, and swap the finished object. // Ok to double initialize this. HttpValueCollection newCollection = HttpValueCollection.Create(this); Interlocked.Exchange(ref _nameValueCollection, newCollection); } return _nameValueCollection; } /// /// Get values associated with a given key. If there are multiple values, they're concatenated. /// public string Get(string key) { return ReadAsNameValueCollection().Get(key); } /// /// Get a value associated with a given key. /// public string[] GetValues(string key) { return ReadAsNameValueCollection().GetValues(key); } public IEnumerator> GetEnumerator() { return _pairs.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { IEnumerable ie = _pairs; return ie.GetEnumerator(); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/FormUrlEncodedJson.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Web.Http; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { /// /// This class provides a low-level API for parsing HTML form URL-encoded data, also known as application/x-www-form-urlencoded /// data. The output of the parser is a instance. /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// internal static class FormUrlEncodedJson { private const string ApplicationFormUrlEncoded = @"application/x-www-form-urlencoded"; private const int MinDepth = 0; private static readonly string[] _emptyPath = new string[] { String.Empty }; /// /// Parses a collection of query string values as a . /// /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// The collection of query string name-value pairs parsed in lexical order. Both names /// and values must be un-escaped so that they don't contain any encoding. /// The corresponding to the given query string values. public static JObject Parse(IEnumerable> nameValuePairs) { return ParseInternal(nameValuePairs, Int32.MaxValue, true); } /// /// Parses a collection of query string values as a . /// /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// The collection of query string name-value pairs parsed in lexical order. Both names /// and values must be un-escaped so that they don't contain any encoding. /// The maximum depth of object graph encoded as x-www-form-urlencoded. /// The corresponding to the given query string values. public static JObject Parse(IEnumerable> nameValuePairs, int maxDepth) { return ParseInternal(nameValuePairs, maxDepth, true); } /// /// Parses a collection of query string values as a . /// /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// The collection of query string name-value pairs parsed in lexical order. Both names /// and values must be un-escaped so that they don't contain any encoding. /// The parsed result or null if parsing failed. /// true if was parsed successfully; otherwise false. public static bool TryParse(IEnumerable> nameValuePairs, out JObject value) { return (value = ParseInternal(nameValuePairs, Int32.MaxValue, false)) != null; } /// /// Parses a collection of query string values as a . /// /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// The collection of query string name-value pairs parsed in lexical order. Both names /// and values must be un-escaped so that they don't contain any encoding. /// The maximum depth of object graph encoded as x-www-form-urlencoded. /// The parsed result or null if parsing failed. /// true if was parsed successfully; otherwise false. public static bool TryParse(IEnumerable> nameValuePairs, int maxDepth, out JObject value) { return (value = ParseInternal(nameValuePairs, maxDepth, false)) != null; } /// /// Parses a collection of query string values as a . /// /// This is a low-level API intended for use by other APIs. It has been optimized for performance and /// is not intended to be called directly from user code. /// The collection of query string name-value pairs parsed in lexical order. Both names /// and values must be un-escaped so that they don't contain any encoding. /// The maximum depth of object graph encoded as x-www-form-urlencoded. /// Indicates whether to throw an exception on error or return false /// The corresponding to the given query string values. private static JObject ParseInternal(IEnumerable> nameValuePairs, int maxDepth, bool throwOnError) { if (nameValuePairs == null) { throw Error.ArgumentNull("nameValuePairs"); } if (maxDepth <= MinDepth) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxDepth", maxDepth, MinDepth + 1); } JObject result = new JObject(); foreach (var nameValuePair in nameValuePairs) { string key = nameValuePair.Key; string value = nameValuePair.Value; // value is preserved, even if it's null, "undefined", "null", String.Empty, etc when converting to JToken. if (key == null) { if (String.IsNullOrEmpty(value)) { if (throwOnError) { throw Error.Argument("nameValuePairs", Properties.Resources.QueryStringNameShouldNotNull); } return null; } string[] path = new string[] { value }; if (!Insert(result, path, null, throwOnError)) { return null; } } else { string[] path = GetPath(key, maxDepth, throwOnError); if (path == null || !Insert(result, path, value, throwOnError)) { return null; } } } FixContiguousArrays(result); return result; } private static string[] GetPath(string key, int maxDepth, bool throwOnError) { Contract.Assert(key != null, "Key cannot be null (this function is only called by Parse if key != null)"); if (String.IsNullOrWhiteSpace(key)) { return _emptyPath; } if (!ValidateQueryString(key, throwOnError)) { return null; } string[] path = key.Split('['); for (int i = 0; i < path.Length; i++) { if (path[i].EndsWith("]", StringComparison.Ordinal)) { path[i] = path[i].Substring(0, path[i].Length - 1); } } // For consistency with JSON, the depth of a[b]=1 is 3 (which is the depth of {a:{b:1}}, given // that in the JSON-XML mapping there's a element wrapping the JSON object: // 1. So if the length of the path is greater than *or equal* to // maxDepth, then we throw. if (path.Length >= maxDepth) { if (throwOnError) { throw Error.Argument(Properties.Resources.MaxDepthExceeded, maxDepth); } return null; } return path; } private static bool ValidateQueryString(string key, bool throwOnError) { bool hasUnMatchedLeftBraket = false; for (int i = 0; i < key.Length; i++) { switch (key[i]) { case '[': if (!hasUnMatchedLeftBraket) { hasUnMatchedLeftBraket = true; } else { if (throwOnError) { throw Error.Argument(Properties.Resources.NestedBracketNotValid, ApplicationFormUrlEncoded, i); } return false; } break; case ']': if (hasUnMatchedLeftBraket) { hasUnMatchedLeftBraket = false; } else { if (throwOnError) { throw Error.Argument(Properties.Resources.UnMatchedBracketNotValid, ApplicationFormUrlEncoded, i); } return false; } break; } } if (hasUnMatchedLeftBraket) { if (throwOnError) { throw Error.Argument(Properties.Resources.NestedBracketNotValid, ApplicationFormUrlEncoded, key.LastIndexOf('[')); } return false; } return true; } private static bool Insert(JObject root, string[] path, string value, bool throwOnError) { // to-do: verify consistent with new parsing, whether single value is in path or value Contract.Assert(root != null, "Root object can't be null"); JObject current = root; JObject parent = null; for (int i = 0; i < path.Length - 1; i++) { if (String.IsNullOrEmpty(path[i])) { if (throwOnError) { throw Error.Argument(Properties.Resources.InvalidArrayInsert, BuildPathString(path, i)); } return false; } if (!((IDictionary)current).ContainsKey(path[i])) { current[path[i]] = new JObject(); } else { // Since the loop goes up to the next-to-last item in the path, if we hit a null // or a primitive, then we have a mismatching node. if (current[path[i]] == null || current[path[i]] is JValue) { if (throwOnError) { throw Error.Argument(Properties.Resources.FormUrlEncodedMismatchingTypes, BuildPathString(path, i)); } return false; } } parent = current; current = current[path[i]] as JObject; } string lastKey = path[path.Length - 1]; if (String.IsNullOrEmpty(lastKey) && path.Length > 1) { if (!AddToArray(parent, path, value, throwOnError)) { return false; } } else { if (current == null) { if (throwOnError) { throw Error.Argument(Properties.Resources.FormUrlEncodedMismatchingTypes, BuildPathString(path, path.Length - 1)); } return false; } if (!AddToObject(current, path, value, throwOnError)) { return false; } } return true; } private static bool AddToObject(JObject obj, string[] path, string value, bool throwOnError) { Contract.Assert(obj != null, "JsonObject cannot be null"); int pathIndex = path.Length - 1; string key = path[pathIndex]; if (((IDictionary)obj).ContainsKey(key)) { if (obj[key] == null || obj[key].Type == JTokenType.Null) { if (throwOnError) { throw Error.Argument(Properties.Resources.FormUrlEncodedMismatchingTypes, BuildPathString(path, pathIndex)); } return false; } bool isRoot = path.Length == 1; if (isRoot) { // jQuery 1.3 behavior, make it into an array(object) if primitive if (obj[key].Type == JTokenType.String) { string oldValue = obj[key].ToObject(); JObject jo = new JObject(); jo.Add("0", oldValue); jo.Add("1", value); obj[key] = jo; } else if (obj[key] is JObject) { // if it was already an object, simply add the value JObject jo = obj[key] as JObject; string index = GetIndex(jo, throwOnError); if (index == null) { return false; } jo.Add(index, value); } } else { if (throwOnError) { throw Error.Argument(Properties.Resources.JQuery13CompatModeNotSupportNestedJson, BuildPathString(path, pathIndex)); } return false; } } else { // if the object didn't contain the key, simply add it now // the null check here is necessary because otherwise the created JValue type will be implictly cast as a string JValue if (value == null) { obj[key] = null; } else { obj[key] = value; } } return true; } // JsonObject passed in is semantically an array private static bool AddToArray(JObject parent, string[] path, string value, bool throwOnError) { Contract.Assert(parent != null, "Parent cannot be null"); Contract.Assert(path.Length >= 2, "The path must be at least 2, one for the ending [], and one for before the '[' (which can be empty)"); string parentPath = path[path.Length - 2]; Contract.Assert(((IDictionary)parent).ContainsKey(parentPath), "It was added on insert to get to this point"); JObject jo = parent[parentPath] as JObject; if (jo == null) { // a[b][c]=1&a[b][]=2 => invalid if (throwOnError) { throw Error.Argument(Properties.Resources.FormUrlEncodedMismatchingTypes, BuildPathString(path, path.Length - 1)); } return false; } else { string index = GetIndex(jo, throwOnError); if (index == null) { return false; } jo.Add(index, value); } return true; } // TODO: consider optimize it by only look at the last one private static string GetIndex(JObject jsonObject, bool throwOnError) { int max = -1; if (jsonObject.Count > 0) { IEnumerable keys = ((IDictionary)jsonObject).Keys; foreach (var key in keys) { int tempInt; if (Int32.TryParse(key, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out tempInt) && tempInt > max) { max = tempInt; } else { if (throwOnError) { throw Error.Argument(Properties.Resources.FormUrlEncodedMismatchingTypes, key); } return null; } } } max++; return max.ToString(CultureInfo.InvariantCulture); } private static void FixContiguousArrays(JToken jv) { JArray ja = jv as JArray; if (ja != null) { for (int i = 0; i < ja.Count; i++) { if (ja[i] != null) { ja[i] = FixSingleContiguousArray(ja[i]); FixContiguousArrays(ja[i]); } } } else { JObject jo = jv as JObject; if (jo != null && jo.Count > 0) { List keys = new List(((IDictionary)jo).Keys); foreach (string key in keys) { if (jo[key] != null) { jo[key] = FixSingleContiguousArray(jo[key]); FixContiguousArrays(jo[key]); } } } } //// do nothing for primitives } private static JToken FixSingleContiguousArray(JToken original) { JObject jo = original as JObject; if (jo != null && jo.Count > 0) { List childKeys = new List(((IDictionary)jo).Keys); List sortedKeys; if (CanBecomeArray(childKeys, out sortedKeys)) { JArray newResult = new JArray(); foreach (string sortedKey in sortedKeys) { newResult.Add(jo[sortedKey]); } return newResult; } } return original; } private static bool CanBecomeArray(List keys, out List sortedKeys) { List intKeys = new List(); sortedKeys = null; bool areContiguousIndices = true; foreach (string key in keys) { int intKey; if (!Int32.TryParse(key, NumberStyles.None, CultureInfo.InvariantCulture, out intKey)) { // if not a non-negative number, it cannot become an array areContiguousIndices = false; break; } string strKey = intKey.ToString(CultureInfo.InvariantCulture); if (!strKey.Equals(key, StringComparison.Ordinal)) { // int.Parse returned true, but it's not really the same number. // It's the case for strings such as "1\0". areContiguousIndices = false; break; } intKeys.Add(new ArrayCandidate(intKey, strKey)); } if (areContiguousIndices) { intKeys.Sort((x, y) => x.Key - y.Key); for (int i = 0; i < intKeys.Count; i++) { if (intKeys[i].Key != i) { areContiguousIndices = false; break; } } } if (areContiguousIndices) { sortedKeys = new List(intKeys.Select(x => x.Value)); } return areContiguousIndices; } private static string BuildPathString(string[] path, int i) { StringBuilder errorPath = new StringBuilder(path[0]); for (int p = 1; p <= i; p++) { errorPath.AppendFormat(CultureInfo.InvariantCulture, "[{0}]", path[p]); } return errorPath.ToString(); } /// /// Class that wraps key-value pairs. /// /// /// This use of this class avoids a FxCop warning CA908 which happens if using various generic types. /// private class ArrayCandidate { /// /// Initializes a new instance of the class. /// /// The key of this instance. /// The value of this instance. public ArrayCandidate(int key, string value) { Key = key; Value = value; } /// /// Gets or sets the key of this instance. /// /// /// The key of this instance. /// public int Key { get; set; } /// /// Gets or sets the value of this instance. /// /// /// The value of this instance. /// public string Value { get; set; } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/FormUrlEncodedMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// class for handling HTML form URL-ended data, also known as application/x-www-form-urlencoded. /// public class FormUrlEncodedMediaTypeFormatter : MediaTypeFormatter { private const int MinBufferSize = 256; private const int DefaultBufferSize = 32 * 1024; private int _readBufferSize = DefaultBufferSize; private int _maxDepth = FormattingUtilities.DefaultMaxDepth; private readonly bool _isDerived; /// /// Initializes a new instance of the class. /// public FormUrlEncodedMediaTypeFormatter() { SupportedMediaTypes.Add(MediaTypeConstants.ApplicationFormUrlEncodedMediaType); _isDerived = GetType() != typeof(FormUrlEncodedMediaTypeFormatter); } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected FormUrlEncodedMediaTypeFormatter(FormUrlEncodedMediaTypeFormatter formatter) : base(formatter) { MaxDepth = formatter.MaxDepth; ReadBufferSize = formatter.ReadBufferSize; _isDerived = GetType() != typeof(FormUrlEncodedMediaTypeFormatter); } /// /// Gets the default media type for HTML Form URL encoded data, namely application/x-www-form-urlencoded. /// /// /// Because is mutable, the value /// returned will be a new instance every time. /// public static MediaTypeHeaderValue DefaultMediaType { get { return MediaTypeConstants.ApplicationFormUrlEncodedMediaType; } } /// /// Gets or sets the maximum depth allowed by this formatter. /// public int MaxDepth { get { return _maxDepth; } set { if (value < FormattingUtilities.DefaultMinDepth) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, FormattingUtilities.DefaultMinDepth); } _maxDepth = value; } } /// /// Gets or sets the size of the buffer when reading the incoming stream. /// /// /// The size of the read buffer. /// public int ReadBufferSize { get { return _readBufferSize; } set { if (value < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, MinBufferSize); } _readBufferSize = value; } } internal override bool CanWriteAnyTypes { get { return _isDerived; } } /// /// Determines whether this can read objects /// of the specified . /// /// The type of object that will be read. /// true if objects of this can be read, otherwise false. public override bool CanReadType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } // Can't read arbitrary types. return type == typeof(FormDataCollection) || FormattingUtilities.IsJTokenType(type); } /// /// Determines whether this can write objects /// of the specified . /// /// The type of object that will be written. /// true if objects of this can be written, otherwise false. public override bool CanWriteType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } return false; } /// /// Called during deserialization to read an object of the specified /// from the specified . /// /// The type of object to read. /// The from which to read. /// The for the content being read. /// The to log events to. /// A whose result will be the object instance that has been read. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } try { return Task.FromResult(ReadFromStream(type, readStream)); } catch (Exception e) { return TaskHelpers.FromError(e); } } private object ReadFromStream(Type type, Stream readStream) { object result; IEnumerable> nameValuePairs = ReadFormUrlEncoded(readStream, ReadBufferSize); if (type == typeof(FormDataCollection)) { result = new FormDataCollection(nameValuePairs); } else if (FormattingUtilities.IsJTokenType(type)) { result = FormUrlEncodedJson.Parse(nameValuePairs, _maxDepth); } else { // Passed us an unsupported type. Should have called CanReadType() first. throw Error.InvalidOperation(Properties.Resources.SerializerCannotSerializeType, GetType().Name, type.Name); } return result; } /// /// Reads all name-value pairs encoded as HTML Form URL encoded data and add them to /// a collection as UNescaped URI strings. /// /// Stream to read from. /// Size of the buffer used to read the contents. /// Collection of name-value pairs. private static IEnumerable> ReadFormUrlEncoded(Stream input, int bufferSize) { Contract.Assert(input != null, "input stream cannot be null"); Contract.Assert(bufferSize >= MinBufferSize, "buffer size cannot be less than MinBufferSize"); byte[] data = new byte[bufferSize]; int bytesRead; bool isFinal = false; List> result = new List>(); FormUrlEncodedParser parser = new FormUrlEncodedParser(result, Int64.MaxValue); ParserState state; while (true) { try { bytesRead = input.Read(data, 0, data.Length); if (bytesRead == 0) { isFinal = true; } } catch (Exception e) { throw Error.InvalidOperation(e, Properties.Resources.ErrorReadingFormUrlEncodedStream); } int bytesConsumed = 0; state = parser.ParseBuffer(data, bytesRead, ref bytesConsumed, isFinal); if (state != ParserState.NeedMoreData && state != ParserState.Done) { throw Error.InvalidOperation(Properties.Resources.FormUrlEncodedParseError, bytesConsumed); } if (isFinal) { return result; } } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/IContentNegotiator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Headers; namespace System.Net.Http.Formatting { /// /// Performs content negotiation. /// This is the process of selecting a response writer (formatter) in compliance with header values in the request. /// public interface IContentNegotiator { /// /// Performs content negotiating by selecting the most appropriate out of the passed in /// for the given that can serialize an object of the given /// . /// /// /// Implementations of this method should call /// on the selected formatter and return the result of that method. /// /// The type to be serialized. /// Request message, which contains the header values used to perform negotiation. /// The set of objects from which to choose. /// The result of the negotiation containing the most appropriate instance, /// or null if there is no appropriate formatter. ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable formatters); } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/IFormatterLogger.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting { /// /// Interface to log events that occur during formatter reads. /// public interface IFormatterLogger { /// /// Logs an error. /// /// The path to the member for which the error is being logged. /// The error message to be logged. void LogError(string errorPath, string errorMessage); /// /// Logs an error. /// /// The path to the member for which the error is being logged. /// The exception to be logged. void LogError(string errorPath, Exception exception); } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/IRequiredMemberSelector.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; namespace System.Net.Http.Formatting { /// /// Interface to determine which data members on a particular type are required. /// public interface IRequiredMemberSelector { /// /// Determines whether a given member is required on deserialization. /// /// The that will be deserialized. /// true if should be treated as a required member, otherwise false. bool IsRequiredMember(MemberInfo member); } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/JsonContractResolver.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace System.Net.Http.Formatting { /// /// Represents the default used by . /// It uses the formatter's to select required members and recognizes /// the type annotation. /// public class JsonContractResolver : DefaultContractResolver { private readonly MediaTypeFormatter _formatter; /// /// Initializes a new instance of the class. /// /// The formatter to use for resolving required members. public JsonContractResolver(MediaTypeFormatter formatter) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } _formatter = formatter; #if !NETSTANDARD1_3 // Need this setting to have [Serializable] types serialized correctly IgnoreSerializableAttribute = false; #endif } // Determines whether a member is required or not and sets the appropriate JsonProperty settings private void ConfigureProperty(MemberInfo member, JsonProperty property) { if (_formatter.RequiredMemberSelector != null && _formatter.RequiredMemberSelector.IsRequiredMember(member)) { property.Required = Required.AllowNull; property.DefaultValueHandling = DefaultValueHandling.Include; property.NullValueHandling = NullValueHandling.Include; } else { property.Required = Required.Default; } } /// protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); ConfigureProperty(member, property); return property; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; #if !NETSTANDARD1_3 // Unnecessary when targeting netstandard1.3. using System.Net.Http.Internal; #endif using System.Runtime.Serialization.Json; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Xml; using Newtonsoft.Json; namespace System.Net.Http.Formatting { /// /// class to handle Json. /// public class JsonMediaTypeFormatter : BaseJsonMediaTypeFormatter { private readonly ConcurrentDictionary _dataContractSerializerCache = new ConcurrentDictionary(); private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.CreateDefaultReaderQuotas(); private readonly RequestHeaderMapping _requestHeaderMapping; /// /// Initializes a new instance of the class. /// public JsonMediaTypeFormatter() { // Set default supported media types SupportedMediaTypes.Add(MediaTypeConstants.ApplicationJsonMediaType); SupportedMediaTypes.Add(MediaTypeConstants.TextJsonMediaType); _requestHeaderMapping = new XmlHttpRequestHeaderMapping(); MediaTypeMappings.Add(_requestHeaderMapping); } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected JsonMediaTypeFormatter(JsonMediaTypeFormatter formatter) : base(formatter) { Contract.Assert(formatter != null); UseDataContractJsonSerializer = formatter.UseDataContractJsonSerializer; Indent = formatter.Indent; } /// /// Gets the default media type for Json, namely "application/json". /// /// /// The default media type does not have any charset parameter as /// the can be configured on a per /// instance basis. /// /// /// Because is mutable, the value /// returned will be a new instance every time. /// public static MediaTypeHeaderValue DefaultMediaType { get { return MediaTypeConstants.ApplicationJsonMediaType; } } /// /// Gets or sets a value indicating whether to use by default. /// /// /// true if use by default; otherwise, false. The default is false. /// public bool UseDataContractJsonSerializer { get; set; } /// /// Gets or sets a value indicating whether to indent elements when writing data. /// public bool Indent { get; set; } /// public sealed override int MaxDepth { get { return base.MaxDepth; } set { base.MaxDepth = value; _readerQuotas.MaxDepth = value; } } /// public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } return new JsonTextReader(new StreamReader(readStream, effectiveEncoding)); } /// public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } JsonWriter jsonWriter = new JsonTextWriter(new StreamWriter(writeStream, effectiveEncoding)); if (Indent) { jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented; } return jsonWriter; } /// public override bool CanReadType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } if (UseDataContractJsonSerializer) { // If there is a registered non-null serializer, we can support this type. DataContractJsonSerializer serializer = _dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(t, throwOnError: false)); // Null means we tested it before and know it is not supported return serializer != null; } else { return base.CanReadType(type); } } /// public override bool CanWriteType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } if (UseDataContractJsonSerializer) { MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type); // If there is a registered non-null serializer, we can support this type. object serializer = _dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(t, throwOnError: false)); // Null means we tested it before and know it is not supported return serializer != null; } else { return base.CanWriteType(type); } } /// public override object ReadFromStream(Type type, Stream readStream, Encoding effectiveEncoding, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } if (UseDataContractJsonSerializer) { DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type); #if NETSTANDARD1_3 // Unreachable when targeting netstandard1.3. Return just to satisfy the compiler. return null; #else // DCS encodings are limited to UTF8, UTF16BE, and UTF16LE. Convert to UTF8 as we read. Stream innerStream = string.Equals(effectiveEncoding.WebName, Utf8Encoding.WebName, StringComparison.OrdinalIgnoreCase) ? new NonClosingDelegatingStream(readStream) : new TranscodingStream(readStream, effectiveEncoding, Utf8Encoding, leaveOpen: true); // XmlDictionaryReader will always dispose of innerStream when we dispose of the reader. using XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(innerStream, Utf8Encoding, _readerQuotas, onClose: null); return dataContractSerializer.ReadObject(reader); #endif } else { return base.ReadFromStream(type, readStream, effectiveEncoding, formatterLogger); } } /// public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (UseDataContractJsonSerializer && Indent) { throw Error.NotSupported(Properties.Resources.UnsupportedIndent, typeof(DataContractJsonSerializer)); } return base.WriteToStreamAsync(type, value, writeStream, content, transportContext, cancellationToken); } /// public override void WriteToStream(Type type, object value, Stream writeStream, Encoding effectiveEncoding) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (effectiveEncoding == null) { throw Error.ArgumentNull("effectiveEncoding"); } if (UseDataContractJsonSerializer) { if (MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type)) { if (value != null) { value = MediaTypeFormatter.GetTypeRemappingConstructor(type).Invoke(new object[] { value }); } } WritePreamble(writeStream, effectiveEncoding); if (string.Equals(effectiveEncoding.WebName, Utf8Encoding.WebName, StringComparison.OrdinalIgnoreCase)) { WriteObject(writeStream, type, value); } else { // JsonReaderWriterFactory is internal and DataContractJsonSerializer only writes UTF8 for the // netstandard1.3 project. In addition, DCS encodings are limited to UTF8, UTF16BE, and UTF16LE. // Convert to UTF8 as we write. using var innerStream = new TranscodingStream(writeStream, effectiveEncoding, Utf8Encoding, leaveOpen: true); WriteObject(innerStream, type, value); } } else { base.WriteToStream(type, value, writeStream, effectiveEncoding); } } private void WriteObject(Stream stream, Type type, object value) { DataContractJsonSerializer dataContractSerializer = GetDataContractSerializer(type); #if !NETSTANDARD1_3 // Unreachable when targeting netstandard1.3. // Do not dispose of the stream. WriteToStream handles that where it's needed. using XmlWriter writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Utf8Encoding, ownsStream: false); dataContractSerializer.WriteObject(writer, value); #endif } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catch all is around an extensibile method")] private DataContractJsonSerializer CreateDataContractSerializer(Type type, bool throwOnError) { Contract.Assert(type != null); #if NETSTANDARD1_3 // XsdDataContractExporter is not supported in netstandard1.3 if (throwOnError) { throw new PlatformNotSupportedException(Error.Format( Properties.Resources.JsonMediaTypeFormatter_DCS_NotSupported, nameof(UseDataContractJsonSerializer))); } else { return null; } #else DataContractJsonSerializer serializer = null; Exception exception = null; try { // Verify that type is a valid data contract by forcing the serializer to try to create a data contract FormattingUtilities.XsdDataContractExporter.GetRootElementName(type); serializer = CreateDataContractSerializer(type); } catch (Exception caught) { exception = caught; } if (serializer == null && throwOnError) { if (exception != null) { throw Error.InvalidOperation(exception, Properties.Resources.SerializerCannotSerializeType, typeof(DataContractJsonSerializer).Name, type.Name); } else { throw Error.InvalidOperation(Properties.Resources.SerializerCannotSerializeType, typeof(DataContractJsonSerializer).Name, type.Name); } } return serializer; #endif } /// /// Called during deserialization to get the . /// /// /// Public for delegating wrappers of this class. Expected to be called only from /// and . /// /// The type of object that will be serialized or deserialized. /// The used to serialize the object. public virtual DataContractJsonSerializer CreateDataContractSerializer(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } return new DataContractJsonSerializer(type); } private DataContractJsonSerializer GetDataContractSerializer(Type type) { Contract.Assert(type != null, "Type cannot be null"); DataContractJsonSerializer serializer = _dataContractSerializerCache.GetOrAdd(type, (t) => CreateDataContractSerializer(type, throwOnError: true)); if (serializer == null) { // A null serializer means the type cannot be serialized throw Error.InvalidOperation(Properties.Resources.SerializerCannotSerializeType, typeof(DataContractJsonSerializer).Name, type.Name); } return serializer; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeConstants.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; namespace System.Net.Http.Formatting { /// /// Constants related to media types. /// internal static class MediaTypeConstants { private static readonly MediaTypeHeaderValue _defaultApplicationXmlMediaType = new MediaTypeHeaderValue("application/xml"); private static readonly MediaTypeHeaderValue _defaultTextXmlMediaType = new MediaTypeHeaderValue("text/xml"); private static readonly MediaTypeHeaderValue _defaultApplicationJsonMediaType = new MediaTypeHeaderValue("application/json"); private static readonly MediaTypeHeaderValue _defaultTextJsonMediaType = new MediaTypeHeaderValue("text/json"); private static readonly MediaTypeHeaderValue _defaultApplicationOctetStreamMediaType = new MediaTypeHeaderValue("application/octet-stream"); private static readonly MediaTypeHeaderValue _defaultApplicationFormUrlEncodedMediaType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); private static readonly MediaTypeHeaderValue _defaultApplicationBsonMediaType = new MediaTypeHeaderValue("application/bson"); /// /// Gets a instance representing application/octet-stream. /// /// /// A new instance representing application/octet-stream. /// public static MediaTypeHeaderValue ApplicationOctetStreamMediaType { get { return _defaultApplicationOctetStreamMediaType.Clone(); } } /// /// Gets a instance representing application/xml. /// /// /// A new instance representing application/xml. /// public static MediaTypeHeaderValue ApplicationXmlMediaType { get { return _defaultApplicationXmlMediaType.Clone(); } } /// /// Gets a instance representing application/json. /// /// /// A new instance representing application/json. /// public static MediaTypeHeaderValue ApplicationJsonMediaType { get { return _defaultApplicationJsonMediaType.Clone(); } } /// /// Gets a instance representing text/xml. /// /// /// A new instance representing text/xml. /// public static MediaTypeHeaderValue TextXmlMediaType { get { return _defaultTextXmlMediaType.Clone(); } } /// /// Gets a instance representing text/json. /// /// /// A new instance representing text/json. /// public static MediaTypeHeaderValue TextJsonMediaType { get { return _defaultTextJsonMediaType.Clone(); } } /// /// Gets a instance representing application/x-www-form-urlencoded. /// /// /// A new instance representing application/x-www-form-urlencoded. /// public static MediaTypeHeaderValue ApplicationFormUrlEncodedMediaType { get { return _defaultApplicationFormUrlEncodedMediaType.Clone(); } } /// /// Gets a instance representing application/bson. /// /// /// A new instance representing application/bson. /// /// /// Not yet a standard. In particular this media type is not currently listed at /// http://www.iana.org/assignments/media-types/application. /// public static MediaTypeHeaderValue ApplicationBsonMediaType { get { return _defaultApplicationBsonMediaType.Clone(); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Base class to handle serializing and deserializing strongly-typed objects using . /// public abstract class MediaTypeFormatter { private protected static readonly Encoding Utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private const int DefaultMinHttpCollectionKeys = 1; private const int DefaultMaxHttpCollectionKeys = 1000; // same default as ASPNET private const string IWellKnownComparerTypeName = "System.IWellKnownStringEqualityComparer, mscorlib, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089"; private static readonly ConcurrentDictionary _delegatingEnumerableCache = new ConcurrentDictionary(); private static ConcurrentDictionary _delegatingEnumerableConstructorCache = new ConcurrentDictionary(); private static Lazy _defaultMaxHttpCollectionKeys = new Lazy(InitializeDefaultCollectionKeySize, true); // Max number of keys is 1000 private static int _maxHttpCollectionKeys = -1; private readonly List _supportedMediaTypes; private readonly List _supportedEncodings; private readonly List _mediaTypeMappings; private IRequiredMemberSelector _requiredMemberSelector; /// /// Initializes a new instance of the class. /// protected MediaTypeFormatter() { _supportedMediaTypes = new List(); SupportedMediaTypes = new MediaTypeHeaderValueCollection(_supportedMediaTypes); _supportedEncodings = new List(); SupportedEncodings = new Collection(_supportedEncodings); _mediaTypeMappings = new List(); MediaTypeMappings = new Collection(_mediaTypeMappings); } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected MediaTypeFormatter(MediaTypeFormatter formatter) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } _supportedMediaTypes = formatter._supportedMediaTypes; SupportedMediaTypes = formatter.SupportedMediaTypes; _supportedEncodings = formatter._supportedEncodings; SupportedEncodings = formatter.SupportedEncodings; _mediaTypeMappings = formatter._mediaTypeMappings; MediaTypeMappings = formatter.MediaTypeMappings; _requiredMemberSelector = formatter._requiredMemberSelector; } /// /// Gets or sets the maximum number of keys stored in a NameValueCollection. /// public static int MaxHttpCollectionKeys { get { if (_maxHttpCollectionKeys < 0) { _maxHttpCollectionKeys = _defaultMaxHttpCollectionKeys.Value; } return _maxHttpCollectionKeys; } set { if (value < DefaultMinHttpCollectionKeys) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, DefaultMinHttpCollectionKeys); } _maxHttpCollectionKeys = value; } } /// /// Gets the mutable collection of elements supported by /// this instance. /// public Collection SupportedMediaTypes { get; private set; } internal List SupportedMediaTypesInternal { get { return _supportedMediaTypes; } } /// /// Gets the mutable collection of character encodings supported by /// this instance. The encodings are /// used when reading or writing data. /// public Collection SupportedEncodings { get; private set; } internal List SupportedEncodingsInternal { get { return _supportedEncodings; } } /// /// Gets the mutable collection of elements used /// by this instance to determine the /// of requests or responses. /// public Collection MediaTypeMappings { get; private set; } internal List MediaTypeMappingsInternal { get { return _mediaTypeMappings; } } /// /// Gets or sets the used to determine required members. /// public virtual IRequiredMemberSelector RequiredMemberSelector { get { return _requiredMemberSelector; } set { _requiredMemberSelector = value; } } internal virtual bool CanWriteAnyTypes { get { return true; } } /// /// Returns a to deserialize an object of the given from the given /// /// /// This implementation throws a . Derived types should override this method if the formatter /// supports reading. /// An implementation of this method should NOT close upon completion. The stream will be closed independently when /// the instance is disposed. /// /// /// The type of the object to deserialize. /// The to read. /// The if available. It may be null. /// The to log events to. /// A whose result will be an object of the given type. /// Derived types need to support reading. /// public virtual Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { throw Error.NotSupported(Properties.Resources.MediaTypeFormatterCannotRead, GetType().Name); } /// /// Returns a to deserialize an object of the given from the given /// /// /// This implementation throws a . Derived types should override this method if the formatter /// supports reading. /// An implementation of this method should NOT close upon completion. The stream will be closed independently when /// the instance is disposed. /// /// /// The type of the object to deserialize. /// The to read. /// The if available. It may be null. /// The to log events to. /// The token to monitor for cancellation requests. /// A whose result will be an object of the given type. /// Derived types need to support reading. /// public virtual Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return TaskHelpers.Canceled(); } return ReadFromStreamAsync(type, readStream, content, formatterLogger); } /// /// Returns a that serializes the given of the given /// to the given . /// /// /// This implementation throws a . Derived types should override this method if the formatter /// supports reading. /// An implementation of this method should NOT close upon completion. The stream will be closed independently when /// the instance is disposed. /// /// /// The type of the object to write. /// The object value to write. It may be null. /// The to which to write. /// The if available. It may be null. /// The if available. It may be null. /// A that will perform the write. /// Derived types need to support writing. /// public virtual Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext) { // HttpContent.SerializeToStreamAsync doesn't take in a CancellationToken. So, there is no easy way to get the CancellationToken // to the formatter while writing response. We are cheating here by passing fake cancellation tokens. We should fix this // when we fix HttpContent. return WriteToStreamAsync(type, value, writeStream, content, transportContext, CancellationToken.None); } /// /// Returns a that serializes the given of the given /// to the given . /// /// /// This implementation throws a . Derived types should override this method if the formatter /// supports reading. /// An implementation of this method should NOT close upon completion. The stream will be closed independently when /// the instance is disposed. /// /// /// The type of the object to write. /// The object value to write. It may be null. /// The to which to write. /// The if available. It may be null. /// The if available. It may be null. /// The token to monitor for cancellation requests. /// A that will perform the write. /// Derived types need to support writing. /// public virtual Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) { throw Error.NotSupported(Properties.Resources.MediaTypeFormatterCannotWrite, GetType().Name); } private static bool TryGetDelegatingType(Type interfaceType, ref Type type) { if (type != null && type.IsInterface() && type.IsGenericType()) { Type genericType = type.ExtractGenericInterface(interfaceType); if (genericType != null) { type = GetOrAddDelegatingType(type, genericType); return true; } } return false; } private static int InitializeDefaultCollectionKeySize() { return Int32.MaxValue; } /// /// This method converts (and interfaces that mandate it) to a for serialization purposes. /// /// The type to potentially be wrapped. If the type is wrapped, it's changed in place. /// Returns true if the type was wrapped; false, otherwise internal static bool TryGetDelegatingTypeForIEnumerableGenericOrSame(ref Type type) { return TryGetDelegatingType(FormattingUtilities.EnumerableInterfaceGenericType, ref type); } /// /// This method converts (and interfaces that mandate it) to a for serialization purposes. /// /// The type to potentially be wrapped. If the type is wrapped, it's changed in place. /// Returns true if the type was wrapped; false, otherwise internal static bool TryGetDelegatingTypeForIQueryableGenericOrSame(ref Type type) { return TryGetDelegatingType(FormattingUtilities.QueryableInterfaceGenericType, ref type); } internal static ConstructorInfo GetTypeRemappingConstructor(Type type) { ConstructorInfo constructorInfo; _delegatingEnumerableConstructorCache.TryGetValue(type, out constructorInfo); return constructorInfo; } /// /// Determines the best amongst the supported encodings /// for reading or writing an HTTP entity body based on the provided . /// /// The content headers provided as part of the request or response. /// The to use when reading the request or writing the response. public Encoding SelectCharacterEncoding(HttpContentHeaders contentHeaders) { // Performance-sensitive Encoding encoding = null; if (contentHeaders != null && contentHeaders.ContentType != null) { // Find encoding based on content type charset parameter string charset = contentHeaders.ContentType.CharSet; if (!String.IsNullOrWhiteSpace(charset)) { for (int i = 0; i < _supportedEncodings.Count; i++) { Encoding supportedEncoding = _supportedEncodings[i]; if (charset.Equals(supportedEncoding.WebName, StringComparison.OrdinalIgnoreCase)) { encoding = supportedEncoding; break; } } } } if (encoding == null) { // We didn't find a character encoding match based on the content headers. // Instead we try getting the default character encoding. if (_supportedEncodings.Count > 0) { encoding = _supportedEncodings[0]; } } if (encoding == null) { // No supported encoding was found so there is no way for us to start reading or writing. throw Error.InvalidOperation(Properties.Resources.MediaTypeFormatterNoEncoding, GetType().Name); } return encoding; } /// /// Sets the default headers for content that will be formatted using this formatter. This method /// is called from the constructor. /// This implementation sets the Content-Type header to the value of if it is /// not null. If it is null it sets the Content-Type to the default media type of this formatter. /// If the Content-Type does not specify a charset it will set it using this formatters configured /// . /// /// /// Subclasses can override this method to set content headers such as Content-Type etc. Subclasses should /// call the base implementation. Subclasses should treat the passed in (if not null) /// as the authoritative media type and use that as the Content-Type. /// /// The type of the object being serialized. See . /// The content headers that should be configured. /// The authoritative media type. Can be null. public virtual void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType) { if (type == null) { throw Error.ArgumentNull("type"); } if (headers == null) { throw Error.ArgumentNull("headers"); } if (mediaType != null) { headers.ContentType = mediaType.Clone(); } // If content type is not set then set it based on supported media types. if (headers.ContentType == null) { MediaTypeHeaderValue defaultMediaType = null; if (_supportedMediaTypes.Count > 0) { defaultMediaType = _supportedMediaTypes[0]; } if (defaultMediaType != null) { headers.ContentType = defaultMediaType.Clone(); } } // If content type charset parameter is not set then set it based on the supported encodings. if (headers.ContentType != null && headers.ContentType.CharSet == null) { Encoding defaultEncoding = null; if (_supportedEncodings.Count > 0) { defaultEncoding = _supportedEncodings[0]; } if (defaultEncoding != null) { headers.ContentType.CharSet = defaultEncoding.WebName; } } } /// /// Returns a specialized instance of the that can handle formatting a response for the given /// parameters. This method is called after a formatter has been selected through content negotiation. /// /// /// The default implementation returns this instance. Derived classes can choose to return a new instance if /// they need to close over any of the parameters. /// /// The type being serialized. /// The request. /// The media type chosen for the serialization. Can be null. /// An instance that can format a response to the given . public virtual MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType) { if (type == null) { throw Error.ArgumentNull("type"); } if (request == null) { throw Error.ArgumentNull("request"); } return this; } /// /// Determines whether this can deserialize /// an object of the specified type. /// /// /// Derived classes must implement this method and indicate if a type can or cannot be deserialized. /// /// The type of object that will be deserialized. /// true if this can deserialize an object of that type; otherwise false. public abstract bool CanReadType(Type type); /// /// Determines whether this can serialize /// an object of the specified type. /// /// /// Derived classes must implement this method and indicate if a type can or cannot be serialized. /// /// The type of object that will be serialized. /// true if this can serialize an object of that type; otherwise false. public abstract bool CanWriteType(Type type); private static Type GetOrAddDelegatingType(Type type, Type genericType) { return _delegatingEnumerableCache.GetOrAdd( type, (typeToRemap) => { // The current method is called by methods that already checked the type for is not null, is generic and is or implements IEnumerable // This retrieves the T type of the IEnumerable interface. Type elementType = genericType.GetGenericArguments()[0]; Type delegatingType = FormattingUtilities.DelegatingEnumerableGenericType.MakeGenericType(elementType); ConstructorInfo delegatingConstructor = delegatingType.GetConstructor(new Type[] { FormattingUtilities.EnumerableInterfaceGenericType.MakeGenericType(elementType) }); _delegatingEnumerableConstructorCache.TryAdd(delegatingType, delegatingConstructor); return delegatingType; }); } /// /// Gets the default value for the specified type. /// public static object GetDefaultValueForType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } if (type.IsValueType()) { return Activator.CreateInstance(type); } return null; } private protected static void WritePreamble(Stream stream, Encoding encoding) { byte[] bytes = encoding.GetPreamble(); if (bytes.Length > 0) { stream.Write(bytes, 0, bytes.Length); } } /// /// Collection class that validates it contains only instances /// that are not null and not media ranges. /// internal class MediaTypeHeaderValueCollection : Collection { private static readonly Type _mediaTypeHeaderValueType = typeof(MediaTypeHeaderValue); internal MediaTypeHeaderValueCollection(IList list) : base(list) { } /// /// Inserts the into the collection at the specified . /// /// The zero-based index at which item should be inserted. /// The object to insert. It cannot be null. protected override void InsertItem(int index, MediaTypeHeaderValue item) { ValidateMediaType(item); base.InsertItem(index, item); } /// /// Replaces the element at the specified . /// /// The zero-based index of the item that should be replaced. /// The new value for the element at the specified index. It cannot be null. protected override void SetItem(int index, MediaTypeHeaderValue item) { ValidateMediaType(item); base.SetItem(index, item); } private static void ValidateMediaType(MediaTypeHeaderValue item) { if (item == null) { throw Error.ArgumentNull("item"); } ParsedMediaTypeHeaderValue parsedMediaType = new ParsedMediaTypeHeaderValue(item); if (parsedMediaType.IsAllMediaRange || parsedMediaType.IsSubtypeMediaRange) { throw Error.Argument("item", Properties.Resources.CannotUseMediaRangeForSupportedMediaType, _mediaTypeHeaderValueType.Name, item.MediaType); } } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeFormatterCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net.Http.Headers; using System.Web.Http; using System.Xml; using System.Xml.Linq; namespace System.Net.Http.Formatting { /// /// Collection class that contains instances. /// public class MediaTypeFormatterCollection : Collection { private static readonly Type _mediaTypeFormatterType = typeof(MediaTypeFormatter); private MediaTypeFormatter[] _writingFormatters; /// /// Initializes a new instance of the class. /// /// /// This collection will be initialized to contain default /// instances for Xml, JsonValue and Json. /// public MediaTypeFormatterCollection() : this(CreateDefaultFormatters()) { } /// /// Initializes a new instance of the class. /// /// A collection of instances to place in the collection. public MediaTypeFormatterCollection(IEnumerable formatters) { VerifyAndSetFormatters(formatters); } internal event EventHandler Changing; /// /// Gets the to use for Xml. /// public XmlMediaTypeFormatter XmlFormatter { get { return Items.OfType().FirstOrDefault(); } } /// /// Gets the to use for Json. /// public JsonMediaTypeFormatter JsonFormatter { get { return Items.OfType().FirstOrDefault(); } } /// /// Gets the to use for application/x-www-form-urlencoded data. /// public FormUrlEncodedMediaTypeFormatter FormUrlEncodedFormatter { get { return Items.OfType().FirstOrDefault(); } } internal MediaTypeFormatter[] WritingFormatters { get { if (_writingFormatters == null) { _writingFormatters = GetWritingFormatters(); } return _writingFormatters; } } /// /// Adds the elements of the specified collection to the end of the . /// /// /// The items that should be added to the end of the . /// The items collection itself cannot be , but it can contain elements that are /// . /// public void AddRange(IEnumerable items) { if (items == null) { throw Error.ArgumentNull("items"); } foreach (MediaTypeFormatter item in items) { Add(item); } } /// /// Inserts the elements of a collection into the at the specified /// index. /// /// The zero-based index at which the new elements should be inserted. /// /// The items that should be inserted into the . The items collection /// itself cannot be , but it can contain elements that are . /// public void InsertRange(int index, IEnumerable items) { if (items == null) { throw Error.ArgumentNull("items"); } foreach (MediaTypeFormatter item in items) { Insert(index++, item); } } /// /// Helper to search a collection for a formatter that can read the .NET type in the given mediaType. /// /// .NET type to read /// media type to match on. /// Formatter that can read the type. Null if no formatter found. public MediaTypeFormatter FindReader(Type type, MediaTypeHeaderValue mediaType) { if (type == null) { throw Error.ArgumentNull("type"); } if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } foreach (MediaTypeFormatter formatter in Items) { if (formatter != null && formatter.CanReadType(type)) { foreach (MediaTypeHeaderValue supportedMediaType in formatter.SupportedMediaTypes) { if (supportedMediaType != null && supportedMediaType.IsSubsetOf(mediaType)) { return formatter; } } } } return null; } /// /// Helper to search a collection for a formatter that can write the .NET type in the given mediaType. /// /// .NET type to read /// media type to match on. /// Formatter that can write the type. Null if no formatter found. public MediaTypeFormatter FindWriter(Type type, MediaTypeHeaderValue mediaType) { if (type == null) { throw Error.ArgumentNull("type"); } if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } foreach (MediaTypeFormatter formatter in Items) { if (formatter != null && formatter.CanWriteType(type)) { foreach (MediaTypeHeaderValue supportedMediaType in formatter.SupportedMediaTypes) { if (supportedMediaType != null && supportedMediaType.IsSubsetOf(mediaType)) { return formatter; } } } } return null; } /// /// Returns true if the type is one of those loosely defined types that should be excluded from validation /// /// .NET to validate /// true if the type should be excluded. public static bool IsTypeExcludedFromValidation(Type type) { return typeof(XmlNode).IsAssignableFrom(type) || typeof(FormDataCollection).IsAssignableFrom(type) || FormattingUtilities.IsJTokenType(type) || typeof(XObject).IsAssignableFrom(type) || typeof(Type).IsAssignableFrom(type) || type == typeof(byte[]); } protected override void ClearItems() { OnChanging(); base.ClearItems(); } protected override void InsertItem(int index, MediaTypeFormatter item) { OnChanging(); base.InsertItem(index, item); } protected override void RemoveItem(int index) { OnChanging(); base.RemoveItem(index); } protected override void SetItem(int index, MediaTypeFormatter item) { OnChanging(); base.SetItem(index, item); } private void OnChanging() { if (Changing != null) { Changing(this, EventArgs.Empty); } // Clear cached state _writingFormatters = null; } private MediaTypeFormatter[] GetWritingFormatters() { return Items.Where((formatter) => formatter != null && formatter.CanWriteAnyTypes).ToArray(); } /// /// Creates a collection of new instances of the default s. /// /// The collection of default instances. private static IEnumerable CreateDefaultFormatters() { return new MediaTypeFormatter[] { new JsonMediaTypeFormatter(), new XmlMediaTypeFormatter() #if NETSTANDARD1_3 // XsdDataContractExporter is not supported in netstandard1.3. Cannot use DCS. { UseXmlSerializer = true } #endif , new FormUrlEncodedMediaTypeFormatter() }; } private void VerifyAndSetFormatters(IEnumerable formatters) { if (formatters == null) { throw Error.ArgumentNull("formatters"); } foreach (MediaTypeFormatter formatter in formatters) { if (formatter == null) { throw Error.Argument("formatters", Properties.Resources.CannotHaveNullInList, _mediaTypeFormatterType.Name); } Add(formatter); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeFormatterExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Extensions for adding items to a . /// [EditorBrowsable(EditorBrowsableState.Never)] public static class MediaTypeFormatterExtensions { /// /// Updates the given 's set of elements /// so that it associates the with s containing /// a specific query parameter and value. /// /// The to receive the new item. /// The name of the query parameter. /// The value assigned to that query parameter. /// The to associate /// with a containing a query string matching /// and . public static void AddQueryStringMapping( this MediaTypeFormatter formatter, string queryStringParameterName, string queryStringParameterValue, MediaTypeHeaderValue mediaType) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); formatter.MediaTypeMappings.Add(mapping); } /// /// Updates the given 's set of elements /// so that it associates the with s containing /// a specific query parameter and value. /// /// The to receive the new item. /// The name of the query parameter. /// The value assigned to that query parameter. /// The media type to associate /// with a containing a query string matching /// and . public static void AddQueryStringMapping( this MediaTypeFormatter formatter, string queryStringParameterName, string queryStringParameterValue, string mediaType) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); formatter.MediaTypeMappings.Add(mapping); } /// /// Updates the given 's set of elements /// so that it associates the with a specific HTTP request header field /// with a specific value. /// /// checks header fields associated with for a match. It does /// not check header fields associated with or instances. /// The to receive the new item. /// Name of the header to match. /// The header value to match. /// The to use when matching . /// if set to true then is /// considered a match if it matches a substring of the actual header value. /// The to associate /// with a entry with a name matching /// and a value matching . public static void AddRequestHeaderMapping( this MediaTypeFormatter formatter, string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring, MediaTypeHeaderValue mediaType) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, valueComparison, isValueSubstring, mediaType); formatter.MediaTypeMappings.Add(mapping); } /// /// Updates the given 's set of elements /// so that it associates the with a specific HTTP request header field /// with a specific value. /// /// checks header fields associated with for a match. It does /// not check header fields associated with or instances. /// The to receive the new item. /// Name of the header to match. /// The header value to match. /// The to use when matching . /// if set to true then is /// considered a match if it matches a substring of the actual header value. /// The media type to associate /// with a entry with a name matching /// and a value matching . public static void AddRequestHeaderMapping( this MediaTypeFormatter formatter, string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring, string mediaType) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, valueComparison, isValueSubstring, mediaType); formatter.MediaTypeMappings.Add(mapping); } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeFormatterMatch.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// This class describes how well a particular matches a request. /// public class MediaTypeFormatterMatch { /// /// Initializes a new instance of the class. /// /// The matching formatter. /// The media type. Can be null in which case the media type application/octet-stream is used. /// The quality of the match. Can be null in which case it is considered a full match with a value of 1.0 /// The kind of match. public MediaTypeFormatterMatch(MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, double? quality, MediaTypeFormatterMatchRanking ranking) { if (formatter == null) { throw Error.ArgumentNull("formatter"); } Formatter = formatter; MediaType = mediaType != null ? mediaType.Clone() : MediaTypeConstants.ApplicationOctetStreamMediaType; Quality = quality ?? FormattingUtilities.Match; Ranking = ranking; } /// /// Gets the media type formatter. /// public MediaTypeFormatter Formatter { get; private set; } /// /// Gets the matched media type. /// public MediaTypeHeaderValue MediaType { get; private set; } /// /// Gets the quality of the match /// public double Quality { get; private set; } /// /// Gets the kind of match that occurred. /// public MediaTypeFormatterMatchRanking Ranking { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeFormatterMatchRanking.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting { /// /// Contains information about the degree to which a matches the /// explicit or implicit preferences found in an incoming request. /// public enum MediaTypeFormatterMatchRanking { /// /// No match was found /// None = 0, /// /// Matched on type meaning that the formatter is able to serialize the type /// MatchOnCanWriteType, /// /// Matched on explicit literal accept header in , /// e.g. "application/json". /// MatchOnRequestAcceptHeaderLiteral, /// /// Matched on explicit subtype range accept header in , /// e.g. "application/*". /// MatchOnRequestAcceptHeaderSubtypeMediaRange, /// /// Matched on explicit all media type range accept header in , /// e.g. "*/*" /// MatchOnRequestAcceptHeaderAllMediaRange, /// /// Matched on after having applied /// the various s. /// MatchOnRequestWithMediaTypeMapping, /// /// Matched on the media type of the of the . /// MatchOnRequestMediaType, } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeHeaderValueExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Linq; using System.Net.Http.Headers; namespace System.Net.Http.Formatting { /// /// Extension methods for . /// internal static class MediaTypeHeaderValueExtensions { /// /// Determines whether two instances match. The instance /// is said to match if and only if /// is a strict subset of the values and parameters of . /// That is, if the media type and media type parameters of are all present /// and match those of then it is a match even though may have additional /// parameters. /// /// The first media type. /// The second media type. /// true if this is a subset of ; false otherwise. public static bool IsSubsetOf(this MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2) { MediaTypeHeaderValueRange mediaType2Range; return IsSubsetOf(mediaType1, mediaType2, out mediaType2Range); } /// /// Determines whether two instances match. The instance /// is said to match if and only if /// is a strict subset of the values and parameters of . /// That is, if the media type and media type parameters of are all present /// and match those of then it is a match even though may have additional /// parameters. /// /// The first media type. /// The second media type. /// Indicates whether is a regular media type, a subtype media range, or a full media range /// true if this is a subset of ; false otherwise. public static bool IsSubsetOf(this MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2, out MediaTypeHeaderValueRange mediaType2Range) { // Performance-sensitive Contract.Assert(mediaType1 != null); if (mediaType2 == null) { mediaType2Range = MediaTypeHeaderValueRange.None; return false; } ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2); mediaType2Range = parsedMediaType2.IsAllMediaRange ? MediaTypeHeaderValueRange.AllMediaRange : parsedMediaType2.IsSubtypeMediaRange ? MediaTypeHeaderValueRange.SubtypeMediaRange : MediaTypeHeaderValueRange.None; if (!parsedMediaType1.TypesEqual(ref parsedMediaType2)) { if (mediaType2Range != MediaTypeHeaderValueRange.AllMediaRange) { return false; } } else if (!parsedMediaType1.SubTypesEqual(ref parsedMediaType2)) { if (mediaType2Range != MediaTypeHeaderValueRange.SubtypeMediaRange) { return false; } } // So far we either have a full match or a subset match. Now check that all of // mediaType1's parameters are present and equal in mediatype2 // Optimize for the common case where the parameters inherit from Collection and cache the count which is faster for Collection. Collection parameters1 = mediaType1.Parameters.AsCollection(); int parameterCount1 = parameters1.Count; Collection parameters2 = mediaType2.Parameters.AsCollection(); int parameterCount2 = parameters2.Count; for (int i = 0; i < parameterCount1; i++) { NameValueHeaderValue parameter1 = parameters1[i]; bool found = false; for (int j = 0; j < parameterCount2; j++) { NameValueHeaderValue parameter2 = parameters2[j]; if (parameter1.Equals(parameter2)) { found = true; break; } } if (!found) { return false; } } return true; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeHeaderValueRange.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting { internal enum MediaTypeHeaderValueRange { /// /// Not a media type range /// None = 0, /// /// A subtype media range, e.g. "application/*". /// SubtypeMediaRange, /// /// An all media range, e.g. "*/*". /// AllMediaRange, } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// An abstract base class used to create an association between or /// instances that have certain characteristics /// and a specific . /// public abstract class MediaTypeMapping { /// /// Initializes a new instance of a with the /// given value. /// /// /// The that is associated with or /// instances that have the given characteristics of the /// . /// protected MediaTypeMapping(MediaTypeHeaderValue mediaType) { if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } MediaType = mediaType; } /// /// Initializes a new instance of a with the /// given value. /// /// /// The that is associated with or /// instances that have the given characteristics of the /// . /// protected MediaTypeMapping(string mediaType) { if (String.IsNullOrWhiteSpace(mediaType)) { throw Error.ArgumentNull("mediaType"); } MediaType = new MediaTypeHeaderValue(mediaType); } /// /// Gets the that is associated with or /// instances that have the given characteristics of the /// . /// public MediaTypeHeaderValue MediaType { get; private set; } /// /// Returns the quality of the match of the /// associated with . /// /// /// The to evaluate for the characteristics /// associated with the /// of the . /// /// /// The quality of the match. It must be between 0.0 and 1.0. /// A value of 0.0 signifies no match. /// A value of 1.0 signifies a complete match. /// public abstract double TryMatchMediaType(HttpRequestMessage request); } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/MediaTypeWithQualityHeaderValueComparer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Net.Http.Headers; namespace System.Net.Http.Formatting { /// Implementation of that can compare accept media type header fields /// based on their quality values (a.k.a q-values). See /// for a comparer for other content negotiation /// header field q-values. internal class MediaTypeWithQualityHeaderValueComparer : IComparer { private static readonly MediaTypeWithQualityHeaderValueComparer _mediaTypeComparer = new MediaTypeWithQualityHeaderValueComparer(); private MediaTypeWithQualityHeaderValueComparer() { } public static MediaTypeWithQualityHeaderValueComparer QualityComparer { get { return _mediaTypeComparer; } } /// /// Compares two based on their quality value (a.k.a their "q-value"). /// Values with identical q-values are considered equal (i.e the result is 0) with the exception that sub-type wild-cards are /// considered less than specific media types and full wild-cards are considered less than sub-type wild-cards. This allows to /// sort a sequence of following their q-values in the order of specific media types, /// sub-type wildcards, and last any full wild-cards. /// /// The first to compare. /// The second to compare. /// public int Compare(MediaTypeWithQualityHeaderValue mediaType1, MediaTypeWithQualityHeaderValue mediaType2) { Contract.Assert(mediaType1 != null, "The 'mediaType1' parameter should not be null."); Contract.Assert(mediaType2 != null, "The 'mediaType2' parameter should not be null."); if (Object.ReferenceEquals(mediaType1, mediaType2)) { return 0; } int returnValue = CompareBasedOnQualityFactor(mediaType1, mediaType2); if (returnValue == 0) { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2); if (!parsedMediaType1.TypesEqual(ref parsedMediaType2)) { if (parsedMediaType1.IsAllMediaRange) { return -1; } else if (parsedMediaType2.IsAllMediaRange) { return 1; } else if (parsedMediaType1.IsSubtypeMediaRange && !parsedMediaType2.IsSubtypeMediaRange) { return -1; } else if (!parsedMediaType1.IsSubtypeMediaRange && parsedMediaType2.IsSubtypeMediaRange) { return 1; } } else if (!parsedMediaType1.SubTypesEqual(ref parsedMediaType2)) { if (parsedMediaType1.IsSubtypeMediaRange) { return -1; } else if (parsedMediaType2.IsSubtypeMediaRange) { return 1; } } } return returnValue; } private static int CompareBasedOnQualityFactor(MediaTypeWithQualityHeaderValue mediaType1, MediaTypeWithQualityHeaderValue mediaType2) { Contract.Assert(mediaType1 != null); Contract.Assert(mediaType2 != null); double mediaType1Quality = mediaType1.Quality ?? FormattingUtilities.Match; double mediaType2Quality = mediaType2.Quality ?? FormattingUtilities.Match; double qualityDifference = mediaType1Quality - mediaType2Quality; if (qualityDifference < 0) { return -1; } else if (qualityDifference > 0) { return 1; } return 0; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/ParsedMediaTypeHeaderValue.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.Net.Http.Headers; namespace System.Net.Http.Formatting { // This type is instanciated by frequently called comparison methods so is very performance sensitive internal struct ParsedMediaTypeHeaderValue { private const char MediaRangeAsterisk = '*'; private const char MediaTypeSubtypeDelimiter = '/'; private readonly string _mediaType; private readonly int _delimiterIndex; private readonly bool _isAllMediaRange; private readonly bool _isSubtypeMediaRange; public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaTypeHeaderValue) { Contract.Assert(mediaTypeHeaderValue != null); string mediaType = _mediaType = mediaTypeHeaderValue.MediaType; _delimiterIndex = mediaType.IndexOf(MediaTypeSubtypeDelimiter); Contract.Assert(_delimiterIndex > 0, "The constructor of the MediaTypeHeaderValue would have failed if there wasn't a type and subtype."); _isAllMediaRange = false; _isSubtypeMediaRange = false; int mediaTypeLength = mediaType.Length; if (_delimiterIndex == mediaTypeLength - 2) { if (mediaType[mediaTypeLength - 1] == MediaRangeAsterisk) { _isSubtypeMediaRange = true; if (_delimiterIndex == 1 && mediaType[0] == MediaRangeAsterisk) { _isAllMediaRange = true; } } } } public bool IsAllMediaRange { get { return _isAllMediaRange; } } public bool IsSubtypeMediaRange { get { return _isSubtypeMediaRange; } } public bool TypesEqual(ref ParsedMediaTypeHeaderValue other) { if (_delimiterIndex != other._delimiterIndex) { return false; } return String.Compare(_mediaType, 0, other._mediaType, 0, _delimiterIndex, StringComparison.OrdinalIgnoreCase) == 0; } public bool SubTypesEqual(ref ParsedMediaTypeHeaderValue other) { int _subTypeLength = _mediaType.Length - _delimiterIndex - 1; if (_subTypeLength != other._mediaType.Length - other._delimiterIndex - 1) { return false; } return String.Compare(_mediaType, _delimiterIndex + 1, other._mediaType, other._delimiterIndex + 1, _subTypeLength, StringComparison.OrdinalIgnoreCase) == 0; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/FormUrlEncodedParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// Buffer-oriented parsing of HTML form URL-ended, also known as application/x-www-form-urlencoded, data. /// internal class FormUrlEncodedParser { private const int MinMessageSize = 1; private long _totalBytesConsumed; private long _maxMessageSize; private NameValueState _nameValueState; private ICollection> _nameValuePairs; private readonly CurrentNameValuePair _currentNameValuePair; /// /// Initializes a new instance of the class. /// /// The collection to which name value pairs are added as they are parsed. /// Maximum length of all the individual name value pairs. public FormUrlEncodedParser(ICollection> nameValuePairs, long maxMessageSize) { // The minimum length which would be an empty buffer if (maxMessageSize < MinMessageSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxMessageSize", maxMessageSize, MinMessageSize); } if (nameValuePairs == null) { throw Error.ArgumentNull("nameValuePairs"); } _nameValuePairs = nameValuePairs; _maxMessageSize = maxMessageSize; _currentNameValuePair = new CurrentNameValuePair(); } private enum NameValueState { Name = 0, Value } /// /// Parse a buffer of URL form-encoded name-value pairs and add them to the collection. /// Bytes are parsed in a consuming manner from the beginning of the buffer meaning that the same bytes can not be /// present in the buffer. /// /// Buffer from where data is read /// Size of buffer /// Offset into buffer /// Indicates whether the end of the URL form-encoded data has been reached. /// State of the parser. Call this method with new data until it reaches a final state. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed, bool isFinal) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; if (bytesConsumed >= bytesReady) { if (isFinal) { parseStatus = CopyCurrent(parseStatus); } // We either can already tell we need more data or we are done return parseStatus; } try { parseStatus = ParseNameValuePairs( buffer, bytesReady, ref bytesConsumed, ref _nameValueState, _maxMessageSize, ref _totalBytesConsumed, _currentNameValuePair, _nameValuePairs); if (isFinal) { parseStatus = CopyCurrent(parseStatus); } } catch (Exception) { parseStatus = ParserState.Invalid; } return parseStatus; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] private static ParserState ParseNameValuePairs( byte[] buffer, int bytesReady, ref int bytesConsumed, ref NameValueState nameValueState, long maximumLength, ref long totalBytesConsumed, CurrentNameValuePair currentNameValuePair, ICollection> nameValuePairs) { Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseNameValuePairs()|(inputBufferLength - bytesParsed) < 0"); Contract.Assert(maximumLength <= 0 || totalBytesConsumed <= maximumLength, "ParseNameValuePairs()|Headers already read exceeds limit."); // Remember where we started. int initialBytesParsed = bytesConsumed; int segmentStart; // Set up parsing status with what will happen if we exceed the buffer. ParserState parseStatus = ParserState.DataTooBig; long effectiveMax = maximumLength <= 0 ? Int64.MaxValue : maximumLength - totalBytesConsumed + initialBytesParsed; if (bytesReady < effectiveMax) { parseStatus = ParserState.NeedMoreData; effectiveMax = bytesReady; } Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max buffer length."); switch (nameValueState) { case NameValueState.Name: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '=' && buffer[bytesConsumed] != '&') { if (++bytesConsumed == effectiveMax) { string name = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentNameValuePair.Name.Append(name); goto quit; } } if (bytesConsumed > segmentStart) { string name = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentNameValuePair.Name.Append(name); } // Check if we got name=value or just name if (buffer[bytesConsumed] == '=') { // Move part the '=' nameValueState = NameValueState.Value; if (++bytesConsumed == effectiveMax) { goto quit; } goto case NameValueState.Value; } else { // Copy parsed name-only to collection currentNameValuePair.CopyNameOnlyTo(nameValuePairs); // Move past the '&' but stay in same state if (++bytesConsumed == effectiveMax) { goto quit; } goto case NameValueState.Name; } case NameValueState.Value: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '&') { if (++bytesConsumed == effectiveMax) { string value = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentNameValuePair.Value.Append(value); goto quit; } } if (bytesConsumed > segmentStart) { string value = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentNameValuePair.Value.Append(value); } // Copy parsed name value pair to collection currentNameValuePair.CopyTo(nameValuePairs); // Move past the '&' nameValueState = NameValueState.Name; if (++bytesConsumed == effectiveMax) { goto quit; } goto case NameValueState.Name; } quit: totalBytesConsumed += bytesConsumed - initialBytesParsed; return parseStatus; } private ParserState CopyCurrent(ParserState parseState) { // Copy parsed name value pair to collection if (_nameValueState == NameValueState.Name) { if (_totalBytesConsumed > 0) { _currentNameValuePair.CopyNameOnlyTo(_nameValuePairs); } } else { _currentNameValuePair.CopyTo(_nameValuePairs); } // We are done (or in an error state) return parseState == ParserState.NeedMoreData ? ParserState.Done : parseState; } /// /// Maintains information about the current header field being parsed. /// private class CurrentNameValuePair { private const int DefaultNameAllocation = 128; private const int DefaultValueAllocation = 2 * 1024; private readonly StringBuilder _name = new StringBuilder(DefaultNameAllocation); private readonly StringBuilder _value = new StringBuilder(DefaultValueAllocation); /// /// Gets the name of the name value pair. /// public StringBuilder Name { get { return _name; } } /// /// Gets the value of the name value pair /// public StringBuilder Value { get { return _value; } } /// /// Copies current name value pair field to the provided collection instance. /// /// The collection to copy into. public void CopyTo(ICollection> nameValuePairs) { string unescapedName = WebUtility.UrlDecode(_name.ToString()); string escapedValue = _value.ToString(); string value = WebUtility.UrlDecode(escapedValue); nameValuePairs.Add(new KeyValuePair(unescapedName, value)); Clear(); } /// /// Copies current name-only to the provided collection instance. /// /// The collection to copy into. public void CopyNameOnlyTo(ICollection> nameValuePairs) { string unescapedName = WebUtility.UrlDecode(_name.ToString()); string value = String.Empty; nameValuePairs.Add(new KeyValuePair(unescapedName, value)); Clear(); } /// /// Clears this instance. /// private void Clear() { _name.Clear(); _value.Clear(); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/HttpRequestHeaderParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// The combines for parsing the HTTP Request Line /// and for parsing each header field. /// internal class HttpRequestHeaderParser { internal const int DefaultMaxRequestLineSize = 2 * 1024; internal const int DefaultMaxHeaderSize = 16 * 1024; // Same default size as IIS has for regular requests private HttpUnsortedRequest _httpRequest; private HttpRequestState _requestStatus = HttpRequestState.RequestLine; private HttpRequestLineParser _requestLineParser; private InternetMessageFormatHeaderParser _headerParser; /// /// Initializes a new instance of the class. /// /// The parsed HTTP request without any header sorting. public HttpRequestHeaderParser(HttpUnsortedRequest httpRequest) : this(httpRequest, DefaultMaxRequestLineSize, DefaultMaxHeaderSize) { } /// /// Initializes a new instance of the class. /// /// The parsed HTTP request without any header sorting. /// The max length of the HTTP request line. /// The max length of the HTTP header. public HttpRequestHeaderParser(HttpUnsortedRequest httpRequest, int maxRequestLineSize, int maxHeaderSize) { if (httpRequest == null) { throw Error.ArgumentNull("httpRequest"); } _httpRequest = httpRequest; // Create request line parser _requestLineParser = new HttpRequestLineParser(_httpRequest, maxRequestLineSize); // Create header parser _headerParser = new InternetMessageFormatHeaderParser(_httpRequest.HttpHeaders, maxHeaderSize); } private enum HttpRequestState { RequestLine = 0, // parsing request line RequestHeaders // reading headers } /// /// Parse an HTTP request header and fill in the instance. /// /// Request buffer from where request is read /// Size of request buffer /// Offset into request buffer /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; ParserState subParseStatus = ParserState.NeedMoreData; switch (_requestStatus) { case HttpRequestState.RequestLine: try { subParseStatus = _requestLineParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { _requestStatus = HttpRequestState.RequestHeaders; subParseStatus = ParserState.NeedMoreData; goto case HttpRequestState.RequestHeaders; } else if (subParseStatus != ParserState.NeedMoreData) { // Report error - either Invalid or DataTooBig parseStatus = subParseStatus; break; } break; // read more data case HttpRequestState.RequestHeaders: if (bytesConsumed >= bytesReady) { // we already can tell we need more data break; } try { subParseStatus = _headerParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { parseStatus = subParseStatus; } else if (subParseStatus != ParserState.NeedMoreData) { parseStatus = subParseStatus; break; } break; // need more data } return parseStatus; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/HttpRequestLineParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// HTTP Request Line parser for parsing the first line (the request line) in an HTTP request. /// internal class HttpRequestLineParser { internal const int MinRequestLineSize = 14; private const int DefaultTokenAllocation = 2 * 1024; private int _totalBytesConsumed; private int _maximumHeaderLength; private HttpRequestLineState _requestLineState; private HttpUnsortedRequest _httpRequest; private StringBuilder _currentToken = new StringBuilder(DefaultTokenAllocation); /// /// Initializes a new instance of the class. /// /// instance where the request line properties will be set as they are parsed. /// Maximum length of HTTP header. public HttpRequestLineParser(HttpUnsortedRequest httpRequest, int maxRequestLineSize) { // The minimum length which would be an empty header terminated by CRLF if (maxRequestLineSize < MinRequestLineSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxRequestLineSize", maxRequestLineSize, MinRequestLineSize); } if (httpRequest == null) { throw Error.ArgumentNull("httpRequest"); } _httpRequest = httpRequest; _maximumHeaderLength = maxRequestLineSize; } private enum HttpRequestLineState { RequestMethod = 0, RequestUri, BeforeVersionNumbers, MajorVersionNumber, MinorVersionNumber, AfterCarriageReturn } /// /// Parse an HTTP request line. /// Bytes are parsed in a consuming manner from the beginning of the request buffer meaning that the same bytes can not be /// present in the request buffer. /// /// Request buffer from where request is read /// Size of request buffer /// Offset into request buffer /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; if (bytesConsumed >= bytesReady) { // We already can tell we need more data return parseStatus; } try { parseStatus = ParseRequestLine( buffer, bytesReady, ref bytesConsumed, ref _requestLineState, _maximumHeaderLength, ref _totalBytesConsumed, _currentToken, _httpRequest); } catch (Exception) { parseStatus = ParserState.Invalid; } return parseStatus; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] private static ParserState ParseRequestLine( byte[] buffer, int bytesReady, ref int bytesConsumed, ref HttpRequestLineState requestLineState, int maximumHeaderLength, ref int totalBytesConsumed, StringBuilder currentToken, HttpUnsortedRequest httpRequest) { Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseRequestLine()|(bytesReady - bytesConsumed) < 0"); Contract.Assert(maximumHeaderLength <= 0 || totalBytesConsumed <= maximumHeaderLength, "ParseRequestLine()|Headers already read exceeds limit."); // Remember where we started. int initialBytesParsed = bytesConsumed; int segmentStart; // Set up parsing status with what will happen if we exceed the buffer. ParserState parseStatus = ParserState.DataTooBig; int effectiveMax = maximumHeaderLength <= 0 ? Int32.MaxValue : (maximumHeaderLength - totalBytesConsumed + bytesConsumed); if (bytesReady < effectiveMax) { parseStatus = ParserState.NeedMoreData; effectiveMax = bytesReady; } Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); switch (requestLineState) { case HttpRequestLineState.RequestMethod: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != ' ') { if (buffer[bytesConsumed] < 0x21 || buffer[bytesConsumed] > 0x7a) { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(method); goto quit; } } if (bytesConsumed > segmentStart) { string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(method); } // Copy value out httpRequest.Method = new HttpMethod(currentToken.ToString()); currentToken.Clear(); // Move past the SP requestLineState = HttpRequestLineState.RequestUri; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpRequestLineState.RequestUri; case HttpRequestLineState.RequestUri: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != ' ') { if (buffer[bytesConsumed] == '\r') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(addr); goto quit; } } if (bytesConsumed > segmentStart) { string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(addr); } // URI validation happens when we create the URI later. if (currentToken.Length == 0) { throw new FormatException(Properties.Resources.HttpMessageParserEmptyUri); } // Copy value out httpRequest.RequestUri = currentToken.ToString(); currentToken.Clear(); // Move past the SP requestLineState = HttpRequestLineState.BeforeVersionNumbers; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpRequestLineState.BeforeVersionNumbers; case HttpRequestLineState.BeforeVersionNumbers: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '/') { if (buffer[bytesConsumed] < 0x21 || buffer[bytesConsumed] > 0x7a) { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(token); goto quit; } } if (bytesConsumed > segmentStart) { string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(token); } // Validate value string version = currentToken.ToString(); if (String.CompareOrdinal(FormattingUtilities.HttpVersionToken, version) != 0) { throw new FormatException(Error.Format(Properties.Resources.HttpInvalidVersion, version, FormattingUtilities.HttpVersionToken)); } currentToken.Clear(); // Move past the '/' requestLineState = HttpRequestLineState.MajorVersionNumber; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpRequestLineState.MajorVersionNumber; case HttpRequestLineState.MajorVersionNumber: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '.') { if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(major); goto quit; } } if (bytesConsumed > segmentStart) { string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(major); } // Move past the "." currentToken.Append('.'); requestLineState = HttpRequestLineState.MinorVersionNumber; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpRequestLineState.MinorVersionNumber; case HttpRequestLineState.MinorVersionNumber: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '\r') { if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(minor); goto quit; } } if (bytesConsumed > segmentStart) { string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(minor); } // Copy out value httpRequest.Version = Version.Parse(currentToken.ToString()); currentToken.Clear(); // Move past the CR requestLineState = HttpRequestLineState.AfterCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpRequestLineState.AfterCarriageReturn; case HttpRequestLineState.AfterCarriageReturn: if (buffer[bytesConsumed] != '\n') { parseStatus = ParserState.Invalid; goto quit; } parseStatus = ParserState.Done; bytesConsumed++; break; } quit: totalBytesConsumed += bytesConsumed - initialBytesParsed; return parseStatus; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/HttpResponseHeaderParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// The combines for parsing the HTTP Status Line /// and for parsing each header field. /// internal class HttpResponseHeaderParser { internal const int DefaultMaxStatusLineSize = 2 * 1024; internal const int DefaultMaxHeaderSize = 16 * 1024; // Same default size as IIS has for HTTP requests private HttpUnsortedResponse _httpResponse; private HttpResponseState _responseStatus = HttpResponseState.StatusLine; private HttpStatusLineParser _statusLineParser; private InternetMessageFormatHeaderParser _headerParser; /// /// Initializes a new instance of the class. /// /// The parsed HTTP response without any header sorting. public HttpResponseHeaderParser(HttpUnsortedResponse httpResponse) : this(httpResponse, DefaultMaxStatusLineSize, DefaultMaxHeaderSize) { } /// /// Initializes a new instance of the class. /// /// The parsed HTTP response without any header sorting. /// The max length of the HTTP status line. /// The max length of the HTTP header. public HttpResponseHeaderParser(HttpUnsortedResponse httpResponse, int maxResponseLineSize, int maxHeaderSize) { if (httpResponse == null) { throw Error.ArgumentNull("httpResponse"); } _httpResponse = httpResponse; // Create status line parser _statusLineParser = new HttpStatusLineParser(_httpResponse, maxResponseLineSize); // Create header parser _headerParser = new InternetMessageFormatHeaderParser(_httpResponse.HttpHeaders, maxHeaderSize); } private enum HttpResponseState { StatusLine = 0, // parsing status line ResponseHeaders // reading headers } /// /// Parse an HTTP response header and fill in the instance. /// /// Response buffer from where response is read /// Size of response buffer /// Offset into response buffer /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; ParserState subParseStatus = ParserState.NeedMoreData; switch (_responseStatus) { case HttpResponseState.StatusLine: try { subParseStatus = _statusLineParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { _responseStatus = HttpResponseState.ResponseHeaders; subParseStatus = ParserState.NeedMoreData; goto case HttpResponseState.ResponseHeaders; } else if (subParseStatus != ParserState.NeedMoreData) { // Report error - either Invalid or DataTooBig parseStatus = subParseStatus; break; } break; // read more data case HttpResponseState.ResponseHeaders: if (bytesConsumed >= bytesReady) { // we already can tell we need more data break; } try { subParseStatus = _headerParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { parseStatus = subParseStatus; } else if (subParseStatus != ParserState.NeedMoreData) { parseStatus = subParseStatus; break; } break; // need more data } return parseStatus; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/HttpStatusLineParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// HTTP Status line parser for parsing the first line (the status line) in an HTTP response. /// internal class HttpStatusLineParser { internal const int MinStatusLineSize = 15; private const int DefaultTokenAllocation = 2 * 1024; private const int MaxStatusCode = 1000; private int _totalBytesConsumed; private int _maximumHeaderLength; private HttpStatusLineState _statusLineState; private HttpUnsortedResponse _httpResponse; private StringBuilder _currentToken = new StringBuilder(DefaultTokenAllocation); /// /// Initializes a new instance of the class. /// /// instance where the response line properties will be set as they are parsed. /// Maximum length of HTTP header. public HttpStatusLineParser(HttpUnsortedResponse httpResponse, int maxStatusLineSize) { // The minimum length which would be an empty header terminated by CRLF if (maxStatusLineSize < MinStatusLineSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxStatusLineSize", maxStatusLineSize, MinStatusLineSize); } if (httpResponse == null) { throw Error.ArgumentNull("httpResponse"); } _httpResponse = httpResponse; _maximumHeaderLength = maxStatusLineSize; } private enum HttpStatusLineState { BeforeVersionNumbers = 0, MajorVersionNumber, MinorVersionNumber, StatusCode, ReasonPhrase, AfterCarriageReturn } /// /// Parse an HTTP status line. /// Bytes are parsed in a consuming manner from the beginning of the response buffer meaning that the same bytes can not be /// present in the response buffer. /// /// Response buffer from where response is read /// Size of response buffer /// Offset into response buffer /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; if (bytesConsumed >= bytesReady) { // We already can tell we need more data return parseStatus; } try { parseStatus = ParseStatusLine( buffer, bytesReady, ref bytesConsumed, ref _statusLineState, _maximumHeaderLength, ref _totalBytesConsumed, _currentToken, _httpResponse); } catch (Exception) { parseStatus = ParserState.Invalid; } return parseStatus; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] private static ParserState ParseStatusLine( byte[] buffer, int bytesReady, ref int bytesConsumed, ref HttpStatusLineState statusLineState, int maximumHeaderLength, ref int totalBytesConsumed, StringBuilder currentToken, HttpUnsortedResponse httpResponse) { Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseRequestLine()|(bytesReady - bytesConsumed) < 0"); Contract.Assert(maximumHeaderLength <= 0 || totalBytesConsumed <= maximumHeaderLength, "ParseRequestLine()|Headers already read exceeds limit."); // Remember where we started. int initialBytesParsed = bytesConsumed; int segmentStart; // Set up parsing status with what will happen if we exceed the buffer. ParserState parseStatus = ParserState.DataTooBig; int effectiveMax = maximumHeaderLength <= 0 ? Int32.MaxValue : (maximumHeaderLength - totalBytesConsumed + bytesConsumed); if (bytesReady < effectiveMax) { parseStatus = ParserState.NeedMoreData; effectiveMax = bytesReady; } Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); switch (statusLineState) { case HttpStatusLineState.BeforeVersionNumbers: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '/') { if (buffer[bytesConsumed] < 0x21 || buffer[bytesConsumed] > 0x7a) { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(token); goto quit; } } if (bytesConsumed > segmentStart) { string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(token); } // Validate value string version = currentToken.ToString(); if (String.CompareOrdinal(FormattingUtilities.HttpVersionToken, version) != 0) { throw new FormatException(Error.Format(Properties.Resources.HttpInvalidVersion, version, FormattingUtilities.HttpVersionToken)); } currentToken.Clear(); // Move past the '/' statusLineState = HttpStatusLineState.MajorVersionNumber; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpStatusLineState.MajorVersionNumber; case HttpStatusLineState.MajorVersionNumber: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '.') { if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(major); goto quit; } } if (bytesConsumed > segmentStart) { string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(major); } // Move past the "." currentToken.Append('.'); statusLineState = HttpStatusLineState.MinorVersionNumber; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpStatusLineState.MinorVersionNumber; case HttpStatusLineState.MinorVersionNumber: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != ' ') { if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(minor); goto quit; } } if (bytesConsumed > segmentStart) { string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(minor); } // Copy out value httpResponse.Version = Version.Parse(currentToken.ToString()); currentToken.Clear(); // Move past the SP statusLineState = HttpStatusLineState.StatusCode; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpStatusLineState.StatusCode; case HttpStatusLineState.StatusCode: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != ' ') { if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9') { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(method); goto quit; } } if (bytesConsumed > segmentStart) { string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(method); } // Copy value out int statusCode = Int32.Parse(currentToken.ToString(), CultureInfo.InvariantCulture); if (statusCode < 100 || statusCode > 1000) { throw new FormatException(Error.Format(Properties.Resources.HttpInvalidStatusCode, statusCode, 100, 1000)); } httpResponse.StatusCode = (HttpStatusCode)statusCode; currentToken.Clear(); // Move past the SP statusLineState = HttpStatusLineState.ReasonPhrase; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpStatusLineState.ReasonPhrase; case HttpStatusLineState.ReasonPhrase: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '\r') { if (buffer[bytesConsumed] < 0x20 || buffer[bytesConsumed] > 0x7a) { parseStatus = ParserState.Invalid; goto quit; } if (++bytesConsumed == effectiveMax) { string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(addr); goto quit; } } if (bytesConsumed > segmentStart) { string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentToken.Append(addr); } // Copy value out httpResponse.ReasonPhrase = currentToken.ToString(); currentToken.Clear(); // Move past the CR statusLineState = HttpStatusLineState.AfterCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HttpStatusLineState.AfterCarriageReturn; case HttpStatusLineState.AfterCarriageReturn: if (buffer[bytesConsumed] != '\n') { parseStatus = ParserState.Invalid; goto quit; } parseStatus = ParserState.Done; bytesConsumed++; break; } quit: totalBytesConsumed += bytesConsumed - initialBytesParsed; return parseStatus; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/InternetMessageFormatHeaderParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Net.Http.Headers; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// Buffer-oriented RFC 5322 style Internet Message Format parser which can be used to pass header /// fields used in HTTP and MIME message entities. /// internal class InternetMessageFormatHeaderParser { internal const int MinHeaderSize = 2; private int _totalBytesConsumed; private int _maxHeaderSize; private HeaderFieldState _headerState; private HttpHeaders _headers; private CurrentHeaderFieldStore _currentHeader; private readonly bool _ignoreHeaderValidation; /// /// Initializes a new instance of the class. /// /// Concrete instance where header fields are added as they are parsed. /// Maximum length of complete header containing all the individual header fields. public InternetMessageFormatHeaderParser(HttpHeaders headers, int maxHeaderSize) : this(headers, maxHeaderSize, ignoreHeaderValidation: false) { } /// /// Initializes a new instance of the class. /// /// /// Concrete instance where header fields are added as they are parsed. /// /// /// Maximum length of complete header containing all the individual header fields. /// /// /// Will validate content and names of headers if set to false. /// public InternetMessageFormatHeaderParser(HttpHeaders headers, int maxHeaderSize, bool ignoreHeaderValidation) { // The minimum length which would be an empty header terminated by CRLF if (maxHeaderSize < InternetMessageFormatHeaderParser.MinHeaderSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxHeaderSize", maxHeaderSize, MinHeaderSize); } if (headers == null) { throw Error.ArgumentNull("headers"); } _headers = headers; _maxHeaderSize = maxHeaderSize; _ignoreHeaderValidation = ignoreHeaderValidation; _currentHeader = new CurrentHeaderFieldStore(); } private enum HeaderFieldState { Name = 0, Value, AfterCarriageReturn, FoldingLine } /// /// Parse a buffer of RFC 5322 style header fields and add them to the collection. /// Bytes are parsed in a consuming manner from the beginning of the buffer meaning that the same bytes can not be /// present in the buffer. /// /// Request buffer from where request is read /// Size of request buffer /// Offset into request buffer /// State of the parser. Call this method with new data until it reaches a final state. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; if (bytesConsumed >= bytesReady) { // We already can tell we need more data return parseStatus; } try { parseStatus = InternetMessageFormatHeaderParser.ParseHeaderFields( buffer, bytesReady, ref bytesConsumed, ref _headerState, _maxHeaderSize, ref _totalBytesConsumed, _currentHeader, _headers, _ignoreHeaderValidation); } catch (Exception) { parseStatus = ParserState.Invalid; } return parseStatus; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] private static ParserState ParseHeaderFields( byte[] buffer, int bytesReady, ref int bytesConsumed, ref HeaderFieldState requestHeaderState, int maximumHeaderLength, ref int totalBytesConsumed, CurrentHeaderFieldStore currentField, HttpHeaders headers, bool ignoreHeaderValidation) { Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseHeaderFields()|(inputBufferLength - bytesParsed) < 0"); Contract.Assert(maximumHeaderLength <= 0 || totalBytesConsumed <= maximumHeaderLength, "ParseHeaderFields()|Headers already read exceeds limit."); // Remember where we started. int initialBytesParsed = bytesConsumed; int segmentStart; // Set up parsing status with what will happen if we exceed the buffer. ParserState parseStatus = ParserState.DataTooBig; int effectiveMax = maximumHeaderLength <= 0 ? Int32.MaxValue : maximumHeaderLength - totalBytesConsumed + initialBytesParsed; if (bytesReady < effectiveMax) { parseStatus = ParserState.NeedMoreData; effectiveMax = bytesReady; } Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); switch (requestHeaderState) { case HeaderFieldState.Name: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != ':') { if (buffer[bytesConsumed] == '\r') { if (!currentField.IsEmpty()) { parseStatus = ParserState.Invalid; goto quit; } else { // Move past the '\r' requestHeaderState = HeaderFieldState.AfterCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.AfterCarriageReturn; } } if (++bytesConsumed == effectiveMax) { string headerFieldName = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentField.Name.Append(headerFieldName); goto quit; } } if (bytesConsumed > segmentStart) { string headerFieldName = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentField.Name.Append(headerFieldName); } // Move past the ':' requestHeaderState = HeaderFieldState.Value; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.Value; case HeaderFieldState.Value: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != '\r') { if (++bytesConsumed == effectiveMax) { string headerFieldValue = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentField.Value.Append(headerFieldValue); goto quit; } } if (bytesConsumed > segmentStart) { string headerFieldValue = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); currentField.Value.Append(headerFieldValue); } // Move past the CR requestHeaderState = HeaderFieldState.AfterCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.AfterCarriageReturn; case HeaderFieldState.AfterCarriageReturn: if (buffer[bytesConsumed] != '\n') { parseStatus = ParserState.Invalid; goto quit; } if (currentField.IsEmpty()) { parseStatus = ParserState.Done; bytesConsumed++; goto quit; } requestHeaderState = HeaderFieldState.FoldingLine; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.FoldingLine; case HeaderFieldState.FoldingLine: if (buffer[bytesConsumed] != ' ' && buffer[bytesConsumed] != '\t') { currentField.CopyTo(headers, ignoreHeaderValidation); requestHeaderState = HeaderFieldState.Name; if (bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.Name; } // Unfold line by inserting SP instead currentField.Value.Append(' '); // Continue parsing header field value requestHeaderState = HeaderFieldState.Value; if (++bytesConsumed == effectiveMax) { goto quit; } goto case HeaderFieldState.Value; } quit: totalBytesConsumed += bytesConsumed - initialBytesParsed; return parseStatus; } /// /// Maintains information about the current header field being parsed. /// private class CurrentHeaderFieldStore { private const int DefaultFieldNameAllocation = 128; private const int DefaultFieldValueAllocation = 2 * 1024; private static readonly char[] _linearWhiteSpace = new char[] { ' ', '\t' }; private readonly StringBuilder _name = new StringBuilder(CurrentHeaderFieldStore.DefaultFieldNameAllocation); private readonly StringBuilder _value = new StringBuilder(CurrentHeaderFieldStore.DefaultFieldValueAllocation); /// /// Gets the header field name. /// public StringBuilder Name { get { return _name; } } /// /// Gets the header field value. /// public StringBuilder Value { get { return _value; } } /// /// Copies current header field to the provided instance. /// /// The headers. /// Set to false to validate headers public void CopyTo(HttpHeaders headers, bool ignoreHeaderValidation) { var name = _name.ToString(); var value = _value.ToString().Trim(CurrentHeaderFieldStore._linearWhiteSpace); if (string.Equals("expires", name, StringComparison.OrdinalIgnoreCase)) { ignoreHeaderValidation = true; } if (ignoreHeaderValidation) { headers.TryAddWithoutValidation(name, value); } else { headers.Add(name, value); } Clear(); } /// /// Determines whether this instance is empty. /// /// /// true if this instance is empty; otherwise, false. /// public bool IsEmpty() { return _name.Length == 0 && _value.Length == 0; } /// /// Clears this instance. /// private void Clear() { _name.Clear(); _value.Clear(); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/MimeMultipartBodyPartParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// Complete MIME multipart parser that combines for parsing the MIME message into individual body parts /// and for parsing each body part into a MIME header and a MIME body. The caller of the parser is returned /// the resulting MIME bodies which can then be written to some output. /// internal class MimeMultipartBodyPartParser : IDisposable { internal const long DefaultMaxMessageSize = Int64.MaxValue; private const int DefaultMaxBodyPartHeaderSize = 4 * 1024; // MIME parser private MimeMultipartParser _mimeParser; private MimeMultipartParser.State _mimeStatus = MimeMultipartParser.State.NeedMoreData; private ArraySegment[] _parsedBodyPart = new ArraySegment[2]; private MimeBodyPart _currentBodyPart; private bool _isFirst = true; // Header field parser private ParserState _bodyPartHeaderStatus = ParserState.NeedMoreData; private int _maxBodyPartHeaderSize; // Stream provider private MultipartStreamProvider _streamProvider; private HttpContent _content; /// /// Initializes a new instance of the class. /// /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. public MimeMultipartBodyPartParser(HttpContent content, MultipartStreamProvider streamProvider) : this(content, streamProvider, DefaultMaxMessageSize, DefaultMaxBodyPartHeaderSize) { } /// /// Initializes a new instance of the class. /// /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. /// The max length of the entire MIME multipart message. /// The max length of the MIME header within each MIME body part. public MimeMultipartBodyPartParser( HttpContent content, MultipartStreamProvider streamProvider, long maxMessageSize, int maxBodyPartHeaderSize) { Contract.Assert(content != null, "content cannot be null."); Contract.Assert(streamProvider != null, "streamProvider cannot be null."); string boundary = ValidateArguments(content, maxMessageSize, true); _mimeParser = new MimeMultipartParser(boundary, maxMessageSize); _currentBodyPart = new MimeBodyPart(streamProvider, maxBodyPartHeaderSize, content); _content = content; _maxBodyPartHeaderSize = maxBodyPartHeaderSize; _streamProvider = streamProvider; } /// /// Determines whether the specified content is MIME multipart content. /// /// The content. /// /// true if the specified content is MIME multipart content; otherwise, false. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to false return.")] public static bool IsMimeMultipartContent(HttpContent content) { Contract.Assert(content != null, "content cannot be null."); try { string boundary = ValidateArguments(content, DefaultMaxMessageSize, false); return boundary != null; } catch (Exception) { return false; } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Parses the data provided and generates parsed MIME body part bodies in the form of which are ready to /// write to the output stream. /// /// The data to parse /// The number of bytes available in the input data /// Parsed instances. public IEnumerable ParseBuffer(byte[] data, int bytesRead) { int bytesConsumed = 0; bool isFinal = false; // There's a special case here - if we've reached the end of the message and there's no optional // CRLF, then we're out of bytes to read, but we have finished the message. // // If IsWaitingForEndOfMessage is true and we're at the end of the stream, then we're going to // call into the parser again with an empty array as the buffer to signal the end of the parse. // Then the final boundary segment will be marked as complete. if (bytesRead == 0 && !_mimeParser.IsWaitingForEndOfMessage) { CleanupCurrentBodyPart(); throw new IOException(Properties.Resources.ReadAsMimeMultipartUnexpectedTermination); } // Make sure we remove an old array segments. _currentBodyPart.Segments.Clear(); while (_mimeParser.CanParseMore(bytesRead, bytesConsumed)) { _mimeStatus = _mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out _parsedBodyPart[0], out _parsedBodyPart[1], out isFinal); if (_mimeStatus != MimeMultipartParser.State.BodyPartCompleted && _mimeStatus != MimeMultipartParser.State.NeedMoreData) { CleanupCurrentBodyPart(); throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartParseError, bytesConsumed, data); } // First body is empty preamble which we just ignore if (_isFirst) { if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted) { _isFirst = false; } continue; } // Parse the two array segments containing parsed body parts that the MIME parser gave us foreach (ArraySegment part in _parsedBodyPart) { if (part.Count == 0) { continue; } if (_bodyPartHeaderStatus != ParserState.Done) { int headerConsumed = part.Offset; _bodyPartHeaderStatus = _currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed); if (_bodyPartHeaderStatus == ParserState.Done) { // Add the remainder as body part content _currentBodyPart.Segments.Add(new ArraySegment(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed)); } else if (_bodyPartHeaderStatus != ParserState.NeedMoreData) { CleanupCurrentBodyPart(); throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartHeaderParseError, headerConsumed, part.Array); } } else { // Add the data as body part content _currentBodyPart.Segments.Add(part); } } if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted) { // If body is completed then swap current body part MimeBodyPart completed = _currentBodyPart; completed.IsComplete = true; completed.IsFinal = isFinal; _currentBodyPart = new MimeBodyPart(_streamProvider, _maxBodyPartHeaderSize, _content); _mimeStatus = MimeMultipartParser.State.NeedMoreData; _bodyPartHeaderStatus = ParserState.NeedMoreData; yield return completed; } else { // Otherwise return what we have yield return _currentBodyPart; } } } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool disposing) { if (disposing) { _mimeParser = null; CleanupCurrentBodyPart(); } } private static string ValidateArguments(HttpContent content, long maxMessageSize, bool throwOnError) { Contract.Assert(content != null, "content cannot be null."); if (maxMessageSize < MimeMultipartParser.MinMessageSize) { if (throwOnError) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxMessageSize", maxMessageSize, MimeMultipartParser.MinMessageSize); } else { return null; } } MediaTypeHeaderValue contentType = content.Headers.ContentType; if (contentType == null) { if (throwOnError) { throw Error.Argument("content", Properties.Resources.ReadAsMimeMultipartArgumentNoContentType, typeof(HttpContent).Name, "multipart/"); } else { return null; } } if (!contentType.MediaType.StartsWith("multipart", StringComparison.OrdinalIgnoreCase)) { if (throwOnError) { throw Error.Argument("content", Properties.Resources.ReadAsMimeMultipartArgumentNoMultipart, typeof(HttpContent).Name, "multipart/"); } else { return null; } } string boundary = null; foreach (NameValueHeaderValue p in contentType.Parameters) { if (p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase)) { boundary = FormattingUtilities.UnquoteToken(p.Value); break; } } if (boundary == null) { if (throwOnError) { throw Error.Argument("content", Properties.Resources.ReadAsMimeMultipartArgumentNoBoundary, typeof(HttpContent).Name, "multipart", "boundary"); } else { return null; } } return boundary; } private void CleanupCurrentBodyPart() { if (_currentBodyPart != null) { _currentBodyPart.Dispose(); _currentBodyPart = null; } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/MimeMultipartParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// Buffer-oriented MIME multipart parser. /// internal class MimeMultipartParser { internal const int MinMessageSize = 10; private const int MaxBoundarySize = 256; private const byte HTAB = 0x09; private const byte SP = 0x20; private const byte CR = 0x0D; private const byte LF = 0x0A; private const byte Dash = 0x2D; private static readonly ArraySegment _emptyBodyPart = new ArraySegment(new byte[0]); private long _totalBytesConsumed; private long _maxMessageSize; private BodyPartState _bodyPartState; private string _boundary; private CurrentBodyPartStore _currentBoundary; /// /// Initializes a new instance of the class. /// /// Message boundary /// Maximum length of entire MIME multipart message. public MimeMultipartParser(string boundary, long maxMessageSize) { // The minimum length which would be an empty message terminated by CRLF if (maxMessageSize < MimeMultipartParser.MinMessageSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxMessageSize", maxMessageSize, MinMessageSize); } if (String.IsNullOrWhiteSpace(boundary)) { throw Error.ArgumentNull("boundary"); } if (boundary.Length > MaxBoundarySize - 10) { throw Error.ArgumentMustBeLessThanOrEqualTo("boundary", boundary.Length, MaxBoundarySize - 10); } if (boundary.EndsWith(" ", StringComparison.Ordinal)) { throw Error.Argument("boundary", Properties.Resources.MimeMultipartParserBadBoundary); } _maxMessageSize = maxMessageSize; _boundary = boundary; _currentBoundary = new CurrentBodyPartStore(_boundary); _bodyPartState = BodyPartState.AfterFirstLineFeed; } public bool IsWaitingForEndOfMessage { get { return _bodyPartState == BodyPartState.AfterBoundary && _currentBoundary != null && _currentBoundary.IsFinal; } } private enum BodyPartState { BodyPart = 0, AfterFirstCarriageReturn, AfterFirstLineFeed, AfterFirstDash, Boundary, AfterBoundary, AfterSecondDash, AfterSecondCarriageReturn } private enum MessageState { Boundary = 0, // about to parse boundary BodyPart, // about to parse body-part CloseDelimiter // about to read close-delimiter } /// /// Represents the overall state of the . /// public enum State { /// /// Need more data /// NeedMoreData = 0, /// /// Parsing of a complete body part succeeded. /// BodyPartCompleted, /// /// Bad data format /// Invalid, /// /// Data exceeds the allowed size /// DataTooBig, } public bool CanParseMore(int bytesRead, int bytesConsumed) { if (bytesConsumed < bytesRead) { // If there's more bytes we haven't parsed, then we can parse more return true; } if (bytesRead == 0 && IsWaitingForEndOfMessage) { // If we're waiting for the end of the message and we've arrived there, we want parse to be called // again so we can mark the parse as complete. // // This can happen when the last boundary segment doesn't have a trailing CRLF. We need to wait until // the end of the message to complete the parse because we need to consume any trailing whitespace that's //present. return true; } return false; } /// /// Parse a MIME multipart message. Bytes are parsed in a consuming /// manner from the beginning of the request buffer meaning that the same bytes can not be /// present in the request buffer. /// /// Request buffer from where request is read /// Size of request buffer /// Offset into request buffer /// Any body part that was considered as a potential MIME multipart boundary but which was in fact part of the body. /// The bulk of the body part. /// Indicates whether the final body part has been found. /// In order to get the complete body part, the caller is responsible for concatenating the contents of the /// and out parameters. /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] public State ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed, out ArraySegment remainingBodyPart, out ArraySegment bodyPart, out bool isFinalBodyPart) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } State parseStatus = State.NeedMoreData; remainingBodyPart = MimeMultipartParser._emptyBodyPart; bodyPart = MimeMultipartParser._emptyBodyPart; isFinalBodyPart = false; try { parseStatus = MimeMultipartParser.ParseBodyPart( buffer, bytesReady, ref bytesConsumed, ref _bodyPartState, _maxMessageSize, ref _totalBytesConsumed, _currentBoundary); } catch (Exception) { parseStatus = State.Invalid; } remainingBodyPart = _currentBoundary.GetDiscardedBoundary(); bodyPart = _currentBoundary.BodyPart; if (parseStatus == State.BodyPartCompleted) { isFinalBodyPart = _currentBoundary.IsFinal; _currentBoundary.ClearAll(); } else { _currentBoundary.ClearBodyPart(); } return parseStatus; } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] private static State ParseBodyPart( byte[] buffer, int bytesReady, ref int bytesConsumed, ref BodyPartState bodyPartState, long maximumMessageLength, ref long totalBytesConsumed, CurrentBodyPartStore currentBodyPart) { Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseBodyPart()|(bytesReady - bytesConsumed) < 0"); Contract.Assert(maximumMessageLength <= 0 || totalBytesConsumed <= maximumMessageLength, "ParseBodyPart()|Message already read exceeds limit."); // Remember where we started. int segmentStart; int initialBytesParsed = bytesConsumed; if (bytesReady == 0 && bodyPartState == BodyPartState.AfterBoundary && currentBodyPart.IsFinal) { // We've seen the end of the stream - the final body part has no trailing CRLF return State.BodyPartCompleted; } // Set up parsing status with what will happen if we exceed the buffer. State parseStatus = State.DataTooBig; long effectiveMax = maximumMessageLength <= 0 ? Int64.MaxValue : (maximumMessageLength - totalBytesConsumed + bytesConsumed); if (effectiveMax == 0) { // effectiveMax is based on our max message size - if we've arrrived at the max size, then we need // to stop parsing. return State.DataTooBig; } if (bytesReady <= effectiveMax) { parseStatus = State.NeedMoreData; effectiveMax = bytesReady; } currentBodyPart.ResetBoundaryOffset(); Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); switch (bodyPartState) { case BodyPartState.BodyPart: while (buffer[bytesConsumed] != MimeMultipartParser.CR) { if (++bytesConsumed == effectiveMax) { goto quit; } } // Remember potential boundary currentBodyPart.AppendBoundary(MimeMultipartParser.CR); // Move past the CR bodyPartState = BodyPartState.AfterFirstCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.AfterFirstCarriageReturn; case BodyPartState.AfterFirstCarriageReturn: if (buffer[bytesConsumed] != MimeMultipartParser.LF) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } // Remember potential boundary currentBodyPart.AppendBoundary(MimeMultipartParser.LF); // Move past the CR bodyPartState = BodyPartState.AfterFirstLineFeed; if (++bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.AfterFirstLineFeed; case BodyPartState.AfterFirstLineFeed: if (buffer[bytesConsumed] == MimeMultipartParser.CR) { // Remember potential boundary currentBodyPart.ResetBoundary(); currentBodyPart.AppendBoundary(MimeMultipartParser.CR); // Move past the CR bodyPartState = BodyPartState.AfterFirstCarriageReturn; if (++bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.AfterFirstCarriageReturn; } if (buffer[bytesConsumed] != MimeMultipartParser.Dash) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } // Remember potential boundary currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); // Move past the Dash bodyPartState = BodyPartState.AfterFirstDash; if (++bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.AfterFirstDash; case BodyPartState.AfterFirstDash: if (buffer[bytesConsumed] != MimeMultipartParser.Dash) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } // Remember potential boundary currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); // Move past the Dash bodyPartState = BodyPartState.Boundary; if (++bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.Boundary; case BodyPartState.Boundary: segmentStart = bytesConsumed; while (buffer[bytesConsumed] != MimeMultipartParser.CR) { if (++bytesConsumed == effectiveMax) { if (currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) { if (currentBodyPart.IsBoundaryComplete()) { // At this point we've seen the end of a boundary segment that is aligned at the end // of the buffer - this might be because we have another segment coming or it might // truly be the end of the message. bodyPartState = BodyPartState.AfterBoundary; } } else { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; } goto quit; } } if (bytesConsumed > segmentStart) { if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } } goto case BodyPartState.AfterBoundary; case BodyPartState.AfterBoundary: // This state means that we just saw the end of a boundary. It might by a 'normal' boundary, in which // case it's followed by optional whitespace and a CRLF. Or it might be the 'final' boundary and will // be followed by '--', optional whitespace and an optional CRLF. if (buffer[bytesConsumed] == MimeMultipartParser.Dash && !currentBodyPart.IsFinal) { currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); if (++bytesConsumed == effectiveMax) { bodyPartState = BodyPartState.AfterSecondDash; goto quit; } goto case BodyPartState.AfterSecondDash; } // Capture optional whitespace segmentStart = bytesConsumed; while (buffer[bytesConsumed] != MimeMultipartParser.CR) { if (++bytesConsumed == effectiveMax) { if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) { // It's an unexpected character currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; } goto quit; } } if (bytesConsumed > segmentStart) { if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } } if (buffer[bytesConsumed] == MimeMultipartParser.CR) { currentBodyPart.AppendBoundary(MimeMultipartParser.CR); if (++bytesConsumed == effectiveMax) { bodyPartState = BodyPartState.AfterSecondCarriageReturn; goto quit; } goto case BodyPartState.AfterSecondCarriageReturn; } else { // It's an unexpected character currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } case BodyPartState.AfterSecondDash: if (buffer[bytesConsumed] == MimeMultipartParser.Dash) { currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); bytesConsumed++; if (currentBodyPart.IsBoundaryComplete()) { Debug.Assert(currentBodyPart.IsFinal); // If we get in here, it means we've see the trailing '--' of the last boundary - in order to consume all of the // remaining bytes, we don't mark the parse as complete again - wait until this method is called again with the // empty buffer to do that. bodyPartState = BodyPartState.AfterBoundary; parseStatus = State.NeedMoreData; goto quit; } else { currentBodyPart.ResetBoundary(); if (bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.BodyPart; } } else { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } case BodyPartState.AfterSecondCarriageReturn: if (buffer[bytesConsumed] != MimeMultipartParser.LF) { currentBodyPart.ResetBoundary(); bodyPartState = BodyPartState.BodyPart; goto case BodyPartState.BodyPart; } currentBodyPart.AppendBoundary(MimeMultipartParser.LF); bytesConsumed++; bodyPartState = BodyPartState.BodyPart; if (currentBodyPart.IsBoundaryComplete()) { parseStatus = State.BodyPartCompleted; goto quit; } else { currentBodyPart.ResetBoundary(); if (bytesConsumed == effectiveMax) { goto quit; } goto case BodyPartState.BodyPart; } } quit: if (initialBytesParsed < bytesConsumed) { int boundaryLength = currentBodyPart.BoundaryDelta; if (boundaryLength > 0 && parseStatus != State.BodyPartCompleted) { currentBodyPart.HasPotentialBoundaryLeftOver = true; } int bodyPartEnd = bytesConsumed - initialBytesParsed - boundaryLength; currentBodyPart.BodyPart = new ArraySegment(buffer, initialBytesParsed, bodyPartEnd); } totalBytesConsumed += bytesConsumed - initialBytesParsed; return parseStatus; } /// /// Maintains information about the current body part being parsed. /// [DebuggerDisplay("{DebuggerToString()}")] private class CurrentBodyPartStore { private const int InitialOffset = 2; private byte[] _boundaryStore = new byte[MaxBoundarySize]; private int _boundaryStoreLength; private byte[] _referenceBoundary = new byte[MaxBoundarySize]; private int _referenceBoundaryLength; private byte[] _boundary = new byte[MaxBoundarySize]; private int _boundaryLength = 0; private ArraySegment _bodyPart = MimeMultipartParser._emptyBodyPart; private bool _isFinal; private bool _isFirst = true; private bool _releaseDiscardedBoundary; private int _boundaryOffset; /// /// Initializes a new instance of the class. /// /// The reference boundary. public CurrentBodyPartStore(string referenceBoundary) { Contract.Assert(referenceBoundary != null); _referenceBoundary[0] = MimeMultipartParser.CR; _referenceBoundary[1] = MimeMultipartParser.LF; _referenceBoundary[2] = MimeMultipartParser.Dash; _referenceBoundary[3] = MimeMultipartParser.Dash; _referenceBoundaryLength = 4 + Encoding.UTF8.GetBytes(referenceBoundary, 0, referenceBoundary.Length, _referenceBoundary, 4); _boundary[0] = MimeMultipartParser.CR; _boundary[1] = MimeMultipartParser.LF; _boundaryLength = CurrentBodyPartStore.InitialOffset; } /// /// Gets or sets a value indicating whether this instance has potential boundary left over. /// /// /// true if this instance has potential boundary left over; otherwise, false. /// public bool HasPotentialBoundaryLeftOver { get; set; } /// /// Gets the boundary delta. /// public int BoundaryDelta { get { return (_boundaryLength - _boundaryOffset > 0) ? _boundaryLength - _boundaryOffset : _boundaryLength; } } /// /// Gets or sets the body part. /// /// /// The body part. /// public ArraySegment BodyPart { get { return _bodyPart; } set { _bodyPart = value; } } /// /// Gets a value indicating whether this body part instance is final. /// /// /// true if this body part instance is final; otherwise, false. /// public bool IsFinal { get { return _isFinal; } } /// /// Resets the boundary offset. /// public void ResetBoundaryOffset() { _boundaryOffset = _boundaryLength; } /// /// Resets the boundary. /// public void ResetBoundary() { // If we had a potential boundary left over then store it so that we don't loose it if (HasPotentialBoundaryLeftOver) { Buffer.BlockCopy(_boundary, 0, _boundaryStore, 0, _boundaryOffset); _boundaryStoreLength = _boundaryOffset; HasPotentialBoundaryLeftOver = false; _releaseDiscardedBoundary = true; } _boundaryLength = 0; _boundaryOffset = 0; } /// /// Appends byte to the current boundary. /// /// The data to append to the boundary. public void AppendBoundary(byte data) { _boundary[_boundaryLength++] = data; } /// /// Appends array of bytes to the current boundary. /// /// The data to append to the boundary. /// The offset into the data. /// The number of bytes to append. public bool AppendBoundary(byte[] data, int offset, int count) { // Check that potential boundary is not bigger than our reference boundary. // Allow for 2 extra characters to include the final boundary which ends with // an additional "--" sequence + plus up to 4 LWS characters (which are allowed). if (_boundaryLength + count > _referenceBoundaryLength + 6) { return false; } int cnt = _boundaryLength; Buffer.BlockCopy(data, offset, _boundary, _boundaryLength, count); _boundaryLength += count; // Verify that boundary matches so far int maxCount = Math.Min(_boundaryLength, _referenceBoundaryLength); for (; cnt < maxCount; cnt++) { if (_boundary[cnt] != _referenceBoundary[cnt]) { return false; } } return true; } /// /// Gets the discarded boundary. /// /// An containing the discarded boundary. public ArraySegment GetDiscardedBoundary() { if (_boundaryStoreLength > 0 && _releaseDiscardedBoundary) { ArraySegment discarded = new ArraySegment(_boundaryStore, 0, _boundaryStoreLength); _boundaryStoreLength = 0; return discarded; } return MimeMultipartParser._emptyBodyPart; } /// /// Determines whether current boundary is valid. /// /// /// true if curent boundary is valid; otherwise, false. /// public bool IsBoundaryValid() { int offset = 0; if (_isFirst) { offset = CurrentBodyPartStore.InitialOffset; } int count = offset; for (; count < _referenceBoundaryLength; count++) { if (_boundary[count] != _referenceBoundary[count]) { return false; } } // Check for final bool boundaryIsFinal = false; if (_boundary[count] == MimeMultipartParser.Dash && _boundary[count + 1] == MimeMultipartParser.Dash) { boundaryIsFinal = true; count += 2; } // Rest of boundary must be ignorable whitespace in order for it to match for (; count < _boundaryLength - 2; count++) { if (_boundary[count] != MimeMultipartParser.SP && _boundary[count] != MimeMultipartParser.HTAB) { return false; } } // We have a valid boundary so whatever we stored in the boundary story is no longer needed _isFinal = boundaryIsFinal; _isFirst = false; return true; } public bool IsBoundaryComplete() { if (!IsBoundaryValid()) { return false; } if (_boundaryLength < _referenceBoundaryLength) { return false; } if (_boundaryLength == _referenceBoundaryLength + 1 && _boundary[_referenceBoundaryLength] == MimeMultipartParser.Dash) { return false; } return true; } /// /// Clears the body part. /// public void ClearBodyPart() { BodyPart = MimeMultipartParser._emptyBodyPart; } /// /// Clears all. /// public void ClearAll() { _releaseDiscardedBoundary = false; HasPotentialBoundaryLeftOver = false; _boundaryLength = 0; _boundaryOffset = 0; _boundaryStoreLength = 0; _isFinal = false; ClearBodyPart(); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used for Debugger Display.")] private string DebuggerToString() { var referenceBoundary = Encoding.UTF8.GetString(_referenceBoundary, 0, _referenceBoundaryLength); var boundary = Encoding.UTF8.GetString(_boundary, 0, _boundaryLength); return String.Format( CultureInfo.InvariantCulture, "Expected: {0} *** Current: {1}", referenceBoundary, boundary); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/Parsers/ParserState.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.Parsers { /// /// Represents the overall state of various parsers. /// internal enum ParserState { /// /// Need more data /// NeedMoreData = 0, /// /// Parsing completed (final) /// Done, /// /// Bad data format (final) /// Invalid, /// /// Data exceeds the allowed size (final) /// DataTooBig, } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/QueryStringMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Class that provides s from query strings. /// public class QueryStringMapping : MediaTypeMapping { private static readonly Type _queryStringMappingType = typeof(QueryStringMapping); /// /// Initializes a new instance of the class. /// /// The name of the query string parameter to match, if present. /// The value of the query string parameter specified by . /// The media type to use if the query parameter specified by is present /// and assigned the value specified by . public QueryStringMapping(string queryStringParameterName, string queryStringParameterValue, string mediaType) : base(mediaType) { Initialize(queryStringParameterName, queryStringParameterValue); } /// /// Initializes a new instance of the class. /// /// The name of the query string parameter to match, if present. /// The value of the query string parameter specified by . /// The to use if the query parameter specified by is present /// and assigned the value specified by . public QueryStringMapping(string queryStringParameterName, string queryStringParameterValue, MediaTypeHeaderValue mediaType) : base(mediaType) { Initialize(queryStringParameterName, queryStringParameterValue); } /// /// Gets the query string parameter name. /// public string QueryStringParameterName { get; private set; } /// /// Gets the query string parameter value. /// public string QueryStringParameterValue { get; private set; } /// /// Returns a value indicating whether the current /// instance can return a from . /// /// The to check. /// If this instance can produce a from /// it returns 1.0 otherwise 0.0. public override double TryMatchMediaType(HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } NameValueCollection queryString = GetQueryString(request.RequestUri); return DoesQueryStringMatch(queryString) ? FormattingUtilities.Match : FormattingUtilities.NoMatch; } private static NameValueCollection GetQueryString(Uri uri) { if (uri == null) { throw Error.InvalidOperation(Properties.Resources.NonNullUriRequiredForMediaTypeMapping, _queryStringMappingType.Name); } return new FormDataCollection(uri).ReadAsNameValueCollection(); } private void Initialize(string queryStringParameterName, string queryStringParameterValue) { if (String.IsNullOrWhiteSpace(queryStringParameterName)) { throw Error.ArgumentNull("queryStringParameterName"); } if (String.IsNullOrWhiteSpace(queryStringParameterValue)) { throw Error.ArgumentNull("queryStringParameterValue"); } QueryStringParameterName = queryStringParameterName.Trim(); QueryStringParameterValue = queryStringParameterValue.Trim(); } private bool DoesQueryStringMatch(NameValueCollection queryString) { if (queryString != null) { foreach (string queryParameter in queryString.AllKeys) { if (String.Equals(queryParameter, QueryStringParameterName, StringComparison.OrdinalIgnoreCase)) { string queryValue = queryString[queryParameter]; if (String.Equals(queryValue, QueryStringParameterValue, StringComparison.OrdinalIgnoreCase)) { return true; } } } } return false; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/RequestHeaderMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// This class provides a mapping from an arbitrary HTTP request header field to a /// used to select instances for handling the entity body of an /// or . /// This class only checks header fields associated with for a match. It does /// not check header fields associated with or instances. /// public class RequestHeaderMapping : MediaTypeMapping { /// /// Initializes a new instance of the class. /// /// Name of the header to match. /// The header value to match. /// The value comparison to use when matching . /// if set to true then is /// considered a match if it matches a substring of the actual header value. /// The media type to use if and /// is considered a match. public RequestHeaderMapping(string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring, string mediaType) : base(mediaType) { Initialize(headerName, headerValue, valueComparison, isValueSubstring); } /// /// Initializes a new instance of the class. /// /// Name of the header to match. /// The header value to match. /// The to use when matching . /// if set to true then is /// considered a match if it matches a substring of the actual header value. /// The to use if and /// is considered a match. public RequestHeaderMapping(string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring, MediaTypeHeaderValue mediaType) : base(mediaType) { Initialize(headerName, headerValue, valueComparison, isValueSubstring); } /// /// Gets the name of the header to match. /// public string HeaderName { get; private set; } /// /// Gets the header value to match. /// public string HeaderValue { get; private set; } /// /// Gets the to use when matching . /// public StringComparison HeaderValueComparison { get; private set; } /// /// Gets a value indicating whether is /// a matched as a substring of the actual header value. /// this instance is value substring. /// /// /// true if is to be matched as a substring; otherwise false. /// public bool IsValueSubstring { get; private set; } /// /// Returns a value indicating whether the current /// instance can return a from . /// /// The to check. /// /// The quality of the match. It must be between 0.0 and 1.0. /// A value of 0.0 signifies no match. /// A value of 1.0 signifies a complete match. /// public override double TryMatchMediaType(HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return MatchHeaderValue(request, HeaderName, HeaderValue, HeaderValueComparison, IsValueSubstring); } private static double MatchHeaderValue(HttpRequestMessage request, string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring) { Contract.Assert(request != null, "request should not be null"); Contract.Assert(headerName != null, "header name should not be null"); Contract.Assert(headerValue != null, "header value should not be null"); IEnumerable values; if (request.Headers.TryGetValues(headerName, out values)) { foreach (string value in values) { if (isValueSubstring) { if (value.IndexOf(headerValue, valueComparison) != -1) { return FormattingUtilities.Match; } } else { if (value.Equals(headerValue, valueComparison)) { return FormattingUtilities.Match; } } } } return FormattingUtilities.NoMatch; } private void Initialize(string headerName, string headerValue, StringComparison valueComparison, bool isValueSubstring) { if (String.IsNullOrWhiteSpace(headerName)) { throw Error.ArgumentNull("headerName"); } if (String.IsNullOrWhiteSpace(headerValue)) { throw Error.ArgumentNull("headerValue"); } StringComparisonHelper.Validate(valueComparison, "valueComparison"); HeaderName = headerName; HeaderValue = headerValue; HeaderValueComparison = valueComparison; IsValueSubstring = isValueSubstring; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/StringComparisonHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// Helper class for validating values. /// internal static class StringComparisonHelper { /// /// Determines whether the specified is defined by the /// enumeration. /// /// The value to verify. /// /// true if the specified options is defined; otherwise, false. /// public static bool IsDefined(StringComparison value) { return value == StringComparison.CurrentCulture || value == StringComparison.CurrentCultureIgnoreCase || #if !NETSTANDARD1_3 value == StringComparison.InvariantCulture || value == StringComparison.InvariantCultureIgnoreCase || #endif value == StringComparison.Ordinal || value == StringComparison.OrdinalIgnoreCase; } /// /// Validates the specified and throws an /// exception if not valid. /// /// The value to validate. /// Name of the parameter to use if throwing exception. public static void Validate(StringComparison value, string parameterName) { if (!IsDefined(value)) { throw Error.InvalidEnumArgument(parameterName, (int)value, typeof(StringComparison)); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/StringWithQualityHeaderValueComparer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Net.Http.Headers; namespace System.Net.Http.Formatting { /// /// Implementation of that can compare content negotiation header fields /// based on their quality values (a.k.a q-values). This applies to values used in accept-charset, /// accept-encoding, accept-language and related header fields with similar syntax rules. See /// for a comparer for media type /// q-values. /// internal class StringWithQualityHeaderValueComparer : IComparer { private static readonly StringWithQualityHeaderValueComparer _qualityComparer = new StringWithQualityHeaderValueComparer(); private StringWithQualityHeaderValueComparer() { } public static StringWithQualityHeaderValueComparer QualityComparer { get { return _qualityComparer; } } /// /// Compares two based on their quality value (a.k.a their "q-value"). /// Values with identical q-values are considered equal (i.e the result is 0) with the exception of wild-card /// values (i.e. a value of "*") which are considered less than non-wild-card values. This allows to sort /// a sequence of following their q-values ending up with any /// wild-cards at the end. /// /// The first value to compare. /// The second value to compare /// The result of the comparison. public int Compare(StringWithQualityHeaderValue stringWithQuality1, StringWithQualityHeaderValue stringWithQuality2) { Contract.Assert(stringWithQuality1 != null); Contract.Assert(stringWithQuality2 != null); double quality1 = stringWithQuality1.Quality ?? FormattingUtilities.Match; double quality2 = stringWithQuality2.Quality ?? FormattingUtilities.Match; double qualityDifference = quality1 - quality2; if (qualityDifference < 0) { return -1; } else if (qualityDifference > 0) { return 1; } if (!String.Equals(stringWithQuality1.Value, stringWithQuality2.Value, StringComparison.OrdinalIgnoreCase)) { if (String.Equals(stringWithQuality1.Value, "*", StringComparison.OrdinalIgnoreCase)) { return -1; } else if (String.Equals(stringWithQuality2.Value, "*", StringComparison.OrdinalIgnoreCase)) { return 1; } } return 0; } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/XmlHttpRequestHeaderMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http.Formatting { /// /// A that maps the X-Requested-With http header field set by AJAX XmlHttpRequest (XHR) /// to the media type application/json if no explicit Accept header fields are present in the request. /// public class XmlHttpRequestHeaderMapping : RequestHeaderMapping { /// /// Initializes a new instance of class /// public XmlHttpRequestHeaderMapping() : base(FormattingUtilities.HttpRequestedWithHeader, FormattingUtilities.HttpRequestedWithHeaderValue, StringComparison.OrdinalIgnoreCase, isValueSubstring: true, mediaType: MediaTypeConstants.ApplicationJsonMediaType) { } /// /// Returns a value indicating whether the current /// instance can return a from . /// /// The to check. /// /// The quality of the match. /// A value of 0.0 signifies no match. /// A value of 1.0 signifies a complete match and that the request was made using XmlHttpRequest without an Accept header. /// public override double TryMatchMediaType(HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } // Accept header trumps XHR mapping. // Accept: */* is equivalent to passing no Accept header. if (request.Headers.Accept.Count == 0 || (request.Headers.Accept.Count == 1 && request.Headers.Accept.First().MediaType.Equals("*/*", StringComparison.Ordinal))) { return base.TryMatchMediaType(request); } else { return FormattingUtilities.NoMatch; } } } } ================================================ FILE: src/System.Net.Http.Formatting/Formatting/XmlMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Net.Http.Internal; using System.Runtime.Serialization; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using System.Xml; using System.Xml.Serialization; namespace System.Net.Http.Formatting { /// /// class to handle Xml. /// public class XmlMediaTypeFormatter : MediaTypeFormatter { private ConcurrentDictionary _serializerCache = new ConcurrentDictionary(); private XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.CreateDefaultReaderQuotas(); /// /// Initializes a new instance of the class. /// public XmlMediaTypeFormatter() { // Set default supported media types SupportedMediaTypes.Add(MediaTypeConstants.ApplicationXmlMediaType); SupportedMediaTypes.Add(MediaTypeConstants.TextXmlMediaType); // Set default supported character encodings SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); SupportedEncodings.Add(new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true)); WriterSettings = new XmlWriterSettings { OmitXmlDeclaration = true, CloseOutput = false, CheckCharacters = false }; } /// /// Initializes a new instance of the class. /// /// The instance to copy settings from. protected XmlMediaTypeFormatter(XmlMediaTypeFormatter formatter) : base(formatter) { UseXmlSerializer = formatter.UseXmlSerializer; WriterSettings = formatter.WriterSettings; MaxDepth = formatter.MaxDepth; } /// /// Gets the default media type for xml, namely "application/xml". /// /// /// /// The default media type does not have any charset parameter as /// the can be configured on a per /// instance basis. /// /// Because is mutable, the value /// returned will be a new instance every time. /// public static MediaTypeHeaderValue DefaultMediaType { get { return MediaTypeConstants.ApplicationXmlMediaType; } } /// /// Gets or sets a value indicating whether to use instead of by default. /// /// /// true if use by default; otherwise, false. The default is false. /// [DefaultValue(false)] public bool UseXmlSerializer { get; set; } /// /// Gets or sets a value indicating whether to indent elements when writing data. /// public bool Indent { get { return WriterSettings.Indent; } set { WriterSettings.Indent = value; } } /// /// Gets the to be used while writing. /// public XmlWriterSettings WriterSettings { get; private set; } /// /// Gets or sets the maximum depth allowed by this formatter. /// public int MaxDepth { get { return _readerQuotas.MaxDepth; } set { if (value < FormattingUtilities.DefaultMinDepth) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, FormattingUtilities.DefaultMinDepth); } _readerQuotas.MaxDepth = value; } } /// /// Registers the to use to read or write /// the specified . /// /// The type of object that will be serialized or deserialized with . /// The instance to use. public void SetSerializer(Type type, XmlObjectSerializer serializer) { VerifyAndSetSerializer(type, serializer); } /// /// Registers the to use to read or write /// the specified type. /// /// The type of object that will be serialized or deserialized with . /// The instance to use. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The T represents a Type parameter.")] public void SetSerializer(XmlObjectSerializer serializer) { SetSerializer(typeof(T), serializer); } /// /// Registers the to use to read or write /// the specified . /// /// The type of objects for which will be used. /// The instance to use. public void SetSerializer(Type type, XmlSerializer serializer) { VerifyAndSetSerializer(type, serializer); } /// /// Registers the to use to read or write /// the specified type. /// /// The type of object that will be serialized or deserialized with . /// The instance to use. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The T represents a Type parameter.")] public void SetSerializer(XmlSerializer serializer) { SetSerializer(typeof(T), serializer); } /// /// Unregisters the serializer currently associated with the given . /// /// /// Unless another serializer is registered for the , a default one will be created. /// /// The type of object whose serializer should be removed. /// true if a serializer was registered for the ; otherwise false. public bool RemoveSerializer(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } object value; return _serializerCache.TryRemove(type, out value); } /// /// Determines whether this can read objects /// of the specified . /// /// The type of object that will be read. /// true if objects of this can be read, otherwise false. public override bool CanReadType(Type type) { if (type == null) { throw Error.ArgumentNull("type"); } // If there is a registered non-null serializer, we can support this type. // Otherwise attempt to create the default serializer. object serializer = GetCachedSerializer(type, throwOnError: false); // Null means we tested it before and know it is not supported return serializer != null; } /// /// Determines whether this can write objects /// of the specified . /// /// The type of object that will be written. /// true if objects of this can be written, otherwise false. public override bool CanWriteType(Type type) { // Performance-sensitive if (type == null) { throw Error.ArgumentNull("type"); } if (UseXmlSerializer) { MediaTypeFormatter.TryGetDelegatingTypeForIEnumerableGenericOrSame(ref type); } else { MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type); } // If there is a registered non-null serializer, we can support this type. object serializer = GetCachedSerializer(type, throwOnError: false); // Null means we tested it before and know it is not supported return serializer != null; } /// /// Called during deserialization to read an object of the specified /// from the specified . /// /// The type of object to read. /// The from which to read. /// The for the content being read. /// The to log events to. /// A whose result will be the object instance that has been read. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public override Task ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { if (type == null) { throw Error.ArgumentNull("type"); } if (readStream == null) { throw Error.ArgumentNull("readStream"); } try { return Task.FromResult(ReadFromStream(type, readStream, content, formatterLogger)); } catch (Exception e) { return TaskHelpers.FromError(e); } } private object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { HttpContentHeaders contentHeaders = content == null ? null : content.Headers; // If content length is 0 then return default value for this type if (contentHeaders != null && contentHeaders.ContentLength == 0) { return GetDefaultValueForType(type); } object serializer = GetDeserializer(type, content); try { using (XmlReader reader = CreateXmlReader(readStream, content)) { XmlSerializer xmlSerializer = serializer as XmlSerializer; if (xmlSerializer != null) { return xmlSerializer.Deserialize(reader); } else { XmlObjectSerializer xmlObjectSerializer = serializer as XmlObjectSerializer; if (xmlObjectSerializer == null) { ThrowInvalidSerializerException(serializer, "GetDeserializer"); } return xmlObjectSerializer.ReadObject(reader); } } } catch (Exception e) { if (formatterLogger == null) { throw; } formatterLogger.LogError(String.Empty, e); return GetDefaultValueForType(type); } } /// /// Called during deserialization to get the XML serializer to use for deserializing objects. /// /// The type of object to deserialize. /// The for the content being read. /// An instance of or to use for deserializing the object. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "The term deserializer is spelled correctly.")] protected internal virtual object GetDeserializer(Type type, HttpContent content) { return GetSerializerForType(type); } /// /// Called during deserialization to get the XML reader to use for reading objects from the stream. /// /// The to read from. /// The for the content being read. /// The to use for reading objects. protected internal virtual XmlReader CreateXmlReader(Stream readStream, HttpContent content) { // Get the character encoding for the content Encoding effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers); // DCS encodings are limited to UTF8, UTF16BE, and UTF16LE. Convert to UTF8 as we read. Stream innerStream = string.Equals(effectiveEncoding.WebName, Utf8Encoding.WebName, StringComparison.OrdinalIgnoreCase) ? new NonClosingDelegatingStream(readStream) : new TranscodingStream(readStream, effectiveEncoding, Utf8Encoding, leaveOpen: true); // XmlDictionaryReader will always dispose of innerStream when caller disposes of the reader. return XmlDictionaryReader.CreateTextReader(innerStream, Utf8Encoding, _readerQuotas, onClose: null); } /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The caught exception type is reflected into a faulted task.")] public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) { if (type == null) { throw Error.ArgumentNull("type"); } if (writeStream == null) { throw Error.ArgumentNull("writeStream"); } if (cancellationToken.IsCancellationRequested) { return TaskHelpers.Canceled(); } try { WriteToStream(type, value, writeStream, content); return TaskHelpers.Completed(); } catch (Exception e) { return TaskHelpers.FromError(e); } } private void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) { bool isRemapped = false; if (UseXmlSerializer) { isRemapped = MediaTypeFormatter.TryGetDelegatingTypeForIEnumerableGenericOrSame(ref type); } else { isRemapped = MediaTypeFormatter.TryGetDelegatingTypeForIQueryableGenericOrSame(ref type); } if (isRemapped && value != null) { value = MediaTypeFormatter.GetTypeRemappingConstructor(type).Invoke(new object[] { value }); } object serializer = GetSerializer(type, value, content); using (XmlWriter writer = CreateXmlWriter(writeStream, content)) { XmlSerializer xmlSerializer = serializer as XmlSerializer; if (xmlSerializer != null) { xmlSerializer.Serialize(writer, value); } else { XmlObjectSerializer xmlObjectSerializer = serializer as XmlObjectSerializer; if (xmlObjectSerializer == null) { ThrowInvalidSerializerException(serializer, "GetSerializer"); } xmlObjectSerializer.WriteObject(writer, value); } } } /// /// Called during serialization to get the XML serializer to use for serializing objects. /// /// The type of object to serialize. /// The object to serialize. /// The for the content being written. /// An instance of or to use for serializing the object. protected internal virtual object GetSerializer(Type type, object value, HttpContent content) { return GetSerializerForType(type); } /// /// Called during serialization to get the XML writer to use for writing objects to the stream. /// /// The to write to. /// The for the content being written. /// The to use for writing objects. protected internal virtual XmlWriter CreateXmlWriter(Stream writeStream, HttpContent content) { Encoding effectiveEncoding = SelectCharacterEncoding(content != null ? content.Headers : null); WritePreamble(writeStream, effectiveEncoding); // DCS encodings are limited to UTF8, UTF16BE, and UTF16LE. Convert to UTF8 as we read. Stream innerStream = string.Equals(effectiveEncoding.WebName, Utf8Encoding.WebName, StringComparison.OrdinalIgnoreCase) ? writeStream : new TranscodingStream(writeStream, effectiveEncoding, Utf8Encoding, leaveOpen: true); XmlWriterSettings writerSettings = WriterSettings.Clone(); writerSettings.Encoding = Utf8Encoding; // Have XmlWriter dispose of innerStream when caller disposes of the writer if using a TranscodingStream. writerSettings.CloseOutput = writeStream != innerStream; return XmlWriter.Create(innerStream, writerSettings); } /// /// Called during deserialization to get the XML serializer. /// /// The type of object that will be serialized or deserialized. /// The used to serialize the object. public virtual XmlSerializer CreateXmlSerializer(Type type) { return new XmlSerializer(type); } /// /// Called during deserialization to get the DataContractSerializer serializer. /// /// The type of object that will be serialized or deserialized. /// The used to serialize the object. public virtual DataContractSerializer CreateDataContractSerializer(Type type) { return new DataContractSerializer(type); } /// /// This method is to support infrastructure and is not intended to be used directly from your code. /// [EditorBrowsable(EditorBrowsableState.Never)] public XmlReader InvokeCreateXmlReader(Stream readStream, HttpContent content) { return CreateXmlReader(readStream, content); } /// /// This method is to support infrastructure and is not intended to be used directly from your code. /// [EditorBrowsable(EditorBrowsableState.Never)] public XmlWriter InvokeCreateXmlWriter(Stream writeStream, HttpContent content) { return CreateXmlWriter(writeStream, content); } /// /// This method is to support infrastructure and is not intended to be used directly from your code. /// [EditorBrowsable(EditorBrowsableState.Never)] public object InvokeGetDeserializer(Type type, HttpContent content) { return GetDeserializer(type, content); } /// /// This method is to support infrastructure and is not intended to be used directly from your code. /// [EditorBrowsable(EditorBrowsableState.Never)] public object InvokeGetSerializer(Type type, object value, HttpContent content) { return GetSerializer(type, value, content); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Since we use an extensible factory method we cannot control the exceptions being thrown")] private object CreateDefaultSerializer(Type type, bool throwOnError) { Contract.Assert(type != null, "type cannot be null."); #if NETSTANDARD1_3 // XsdDataContractExporter is not supported in netstandard1.3 if (!UseXmlSerializer) { if (throwOnError) { throw new PlatformNotSupportedException(Error.Format( Properties.Resources.XmlMediaTypeFormatter_DCS_NotSupported, nameof(UseXmlSerializer))); } else { return null; } } #endif Exception exception = null; object serializer = null; try { if (UseXmlSerializer) { serializer = CreateXmlSerializer(type); } else { #if !NETSTANDARD1_3 // Unreachable when targeting netstandard1.3. // Verify that type is a valid data contract by forcing the serializer to try to create a data contract FormattingUtilities.XsdDataContractExporter.GetRootElementName(type); serializer = CreateDataContractSerializer(type); #endif } } catch (Exception caught) { exception = caught; } if (serializer == null && throwOnError) { if (exception != null) { throw Error.InvalidOperation(exception, Properties.Resources.SerializerCannotSerializeType, UseXmlSerializer ? typeof(XmlSerializer).Name : typeof(DataContractSerializer).Name, type.Name); } else { throw Error.InvalidOperation(Properties.Resources.SerializerCannotSerializeType, UseXmlSerializer ? typeof(XmlSerializer).Name : typeof(DataContractSerializer).Name, type.Name); } } return serializer; } private object GetCachedSerializer(Type type, bool throwOnError) { // Performance-sensitive object serializer; if (!_serializerCache.TryGetValue(type, out serializer)) { // Race condition on creation has no side effects serializer = CreateDefaultSerializer(type, throwOnError); _serializerCache.TryAdd(type, serializer); } return serializer; } private void VerifyAndSetSerializer(Type type, object serializer) { if (type == null) { throw Error.ArgumentNull("type"); } if (serializer == null) { throw Error.ArgumentNull("serializer"); } SetSerializerInternal(type, serializer); } private void SetSerializerInternal(Type type, object serializer) { Contract.Assert(type != null, "type cannot be null."); Contract.Assert(serializer != null, "serializer cannot be null."); _serializerCache.AddOrUpdate(type, serializer, (key, value) => serializer); } private object GetSerializerForType(Type type) { // Performance-sensitive Contract.Assert(type != null, "Type cannot be null"); object serializer = GetCachedSerializer(type, throwOnError: true); if (serializer == null) { // A null serializer indicates the type has already been tested // and found unsupportable. throw Error.InvalidOperation(Properties.Resources.SerializerCannotSerializeType, UseXmlSerializer ? typeof(XmlSerializer).Name : typeof(DataContractSerializer).Name, type.Name); } Contract.Assert(serializer is XmlSerializer || serializer is XmlObjectSerializer, "Only XmlSerializer or XmlObjectSerializer are supported."); return serializer; } private static void ThrowInvalidSerializerException(object serializer, string getSerializerMethodName) { if (serializer == null) { throw Error.InvalidOperation(Properties.Resources.XmlMediaTypeFormatter_NullReturnedSerializer, getSerializerMethodName); } else { throw Error.InvalidOperation(Properties.Resources.XmlMediaTypeFormatter_InvalidSerializerType, serializer.GetType().Name, getSerializerMethodName); } } } } ================================================ FILE: src/System.Net.Http.Formatting/FormattingUtilities.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Runtime.Serialization; using System.Xml; using Newtonsoft.Json.Linq; namespace System.Net.Http { /// /// Provides various internal utility functions /// internal static class FormattingUtilities { // Supported date formats for input. private static readonly string[] dateFormats = new string[] { // "r", // RFC 1123, required output format but too strict for input "ddd, d MMM yyyy H:m:s 'GMT'", // RFC 1123 (r, except it allows both 1 and 01 for date and time) "ddd, d MMM yyyy H:m:s", // RFC 1123, no zone - assume GMT "d MMM yyyy H:m:s 'GMT'", // RFC 1123, no day-of-week "d MMM yyyy H:m:s", // RFC 1123, no day-of-week, no zone "ddd, d MMM yy H:m:s 'GMT'", // RFC 1123, short year "ddd, d MMM yy H:m:s", // RFC 1123, short year, no zone "d MMM yy H:m:s 'GMT'", // RFC 1123, no day-of-week, short year "d MMM yy H:m:s", // RFC 1123, no day-of-week, short year, no zone "dddd, d'-'MMM'-'yy H:m:s 'GMT'", // RFC 850, short year "dddd, d'-'MMM'-'yy H:m:s", // RFC 850 no zone "ddd, d'-'MMM'-'yyyy H:m:s 'GMT'", // RFC 850, long year "ddd MMM d H:m:s yyyy", // ANSI C's asctime() format "ddd, d MMM yyyy H:m:s zzz", // RFC 5322 "ddd, d MMM yyyy H:m:s", // RFC 5322 no zone "d MMM yyyy H:m:s zzz", // RFC 5322 no day-of-week "d MMM yyyy H:m:s", // RFC 5322 no day-of-week, no zone }; // Valid header token characters are within the range 0x20 < c < 0x7F excluding the following characters private const string NonTokenChars = "()<>@,;:\\\"/[]?={}"; /// /// Quality factor to indicate a perfect match. /// public const double Match = 1.0; /// /// Quality factor to indicate no match. /// public const double NoMatch = 0.0; /// /// The default max depth for our formatter is 256 /// public const int DefaultMaxDepth = 256; /// /// The default min depth for our formatter is 1 /// public const int DefaultMinDepth = 1; /// /// HTTP X-Requested-With header field name /// public const string HttpRequestedWithHeader = @"x-requested-with"; /// /// HTTP X-Requested-With header field value /// public const string HttpRequestedWithHeaderValue = @"XMLHttpRequest"; /// /// HTTP Host header field name /// public const string HttpHostHeader = "Host"; /// /// HTTP Version token /// public const string HttpVersionToken = "HTTP"; /// /// A representing . /// public static readonly Type HttpRequestMessageType = typeof(HttpRequestMessage); /// /// A representing . /// public static readonly Type HttpResponseMessageType = typeof(HttpResponseMessage); /// /// A representing . /// public static readonly Type HttpContentType = typeof(HttpContent); /// /// A representing . /// public static readonly Type DelegatingEnumerableGenericType = typeof(DelegatingEnumerable<>); /// /// A representing . /// public static readonly Type EnumerableInterfaceGenericType = typeof(IEnumerable<>); /// /// A representing . /// public static readonly Type QueryableInterfaceGenericType = typeof(IQueryable<>); #if !NETSTANDARD1_3 // XsdDataContractExporter is not supported in netstandard1.3 /// /// An instance of . /// public static readonly XsdDataContractExporter XsdDataContractExporter = new XsdDataContractExporter(); #endif /// /// Determines whether is a type. /// /// The type to test. /// /// true if is a type; otherwise, false. /// public static bool IsJTokenType(Type type) { return typeof(JToken).IsAssignableFrom(type); } /// /// Creates an empty instance. The only way is to get it from a dummy /// instance. /// /// The created instance. public static HttpContentHeaders CreateEmptyContentHeaders() { HttpContent tempContent = null; HttpContentHeaders contentHeaders = null; try { tempContent = new StringContent(String.Empty); contentHeaders = tempContent.Headers; contentHeaders.Clear(); } finally { // We can dispose the content without touching the headers if (tempContent != null) { tempContent.Dispose(); } } return contentHeaders; } /// /// Create a default reader quotas with a default depth quota of 1K /// /// public static XmlDictionaryReaderQuotas CreateDefaultReaderQuotas() { return new XmlDictionaryReaderQuotas() { MaxArrayLength = Int32.MaxValue, MaxBytesPerRead = Int32.MaxValue, MaxDepth = DefaultMaxDepth, MaxNameTableCharCount = Int32.MaxValue, MaxStringContentLength = Int32.MaxValue }; } /// /// Remove bounding quotes on a token if present /// /// Token to unquote. /// Unquoted token. public static string UnquoteToken(string token) { if (String.IsNullOrWhiteSpace(token)) { return token; } if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1) { return token.Substring(1, token.Length - 2); } return token; } public static bool ValidateHeaderToken(string token) { if (token == null) { return false; } foreach (char c in token) { if (c < 0x21 || c > 0x7E || NonTokenChars.IndexOf(c) != -1) { return false; } } return true; } public static string DateToString(DateTimeOffset dateTime) { // Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo) return dateTime.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture); } public static bool TryParseDate(string input, out DateTimeOffset result) { return DateTimeOffset.TryParseExact(input, dateFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal, out result); } /// /// Parses valid integer strings with no leading signs, whitespace or other /// /// The value to parse /// The result /// True if value was valid; false otherwise. public static bool TryParseInt32(string value, out int result) { return Int32.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result); } } } ================================================ FILE: src/System.Net.Http.Formatting/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Headers", Justification = "We follow the layout of System.Net.Http.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Net.Http.Handlers", Justification = "Handlers provide an extensibility hook which we want to keep in a separate namespace.")] // Some resources are specific to the netstandard1.3 assembly [assembly: SuppressMessage("Microsoft.Web.FxCop", "MW1000:UnusedResourceUsageRule", Justification = "There are a few unused resources due to missing netstandard1.3 features.")] ================================================ FILE: src/System.Net.Http.Formatting/Handlers/HttpProgressEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; namespace System.Net.Http.Handlers { /// /// Provides data for the events generated by . /// public class HttpProgressEventArgs : ProgressChangedEventArgs { /// /// Initializes a new instance of the with the parameters given. /// /// The percent completed of the overall exchange. /// Any user state provided as part of reading or writing the data. /// The current number of bytes either received or sent. /// The total number of bytes expected to be received or sent. public HttpProgressEventArgs(int progressPercentage, object userToken, long bytesTransferred, long? totalBytes) : base(progressPercentage, userToken) { BytesTransferred = bytesTransferred; TotalBytes = totalBytes; } /// /// Gets the current number of bytes transferred. /// public long BytesTransferred { get; private set; } /// /// Gets the total number of expected bytes to be sent or received. If the number is not known then this is null. /// public long? TotalBytes { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/Handlers/ProgressContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.IO; using System.Threading.Tasks; namespace System.Net.Http.Handlers { /// /// Wraps an inner in order to insert a on writing data. /// internal class ProgressContent : HttpContent { private readonly HttpContent _innerContent; private readonly ProgressMessageHandler _handler; private readonly HttpRequestMessage _request; public ProgressContent(HttpContent innerContent, ProgressMessageHandler handler, HttpRequestMessage request) { Contract.Assert(innerContent != null); Contract.Assert(handler != null); Contract.Assert(request != null); _innerContent = innerContent; _handler = handler; _request = request; innerContent.Headers.CopyTo(Headers); } protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { ProgressStream progressStream = new ProgressStream(stream, _handler, _request, response: null); return _innerContent.CopyToAsync(progressStream); } protected override bool TryComputeLength(out long length) { long? contentLength = _innerContent.Headers.ContentLength; if (contentLength.HasValue) { length = contentLength.Value; return true; } length = -1; return false; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _innerContent.Dispose(); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Handlers/ProgressMessageHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Threading; using System.Threading.Tasks; namespace System.Net.Http.Handlers { /// /// The provides a mechanism for getting progress event notifications /// when sending and receiving data in connection with exchanging HTTP requests and responses. /// Register event handlers for the events and /// to see events for data being sent and received. /// public class ProgressMessageHandler : DelegatingHandler { /// /// Initializes a new instance of the class. /// public ProgressMessageHandler() { } /// /// Initializes a new instance of the class. /// /// The inner handler to which this handler submits requests. public ProgressMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler) { } /// /// Occurs every time the client sending data is making progress. /// public event EventHandler HttpSendProgress; /// /// Occurs every time the client receiving data is making progress. /// public event EventHandler HttpReceiveProgress; protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { AddRequestProgress(request); HttpResponseMessage response = await base.SendAsync(request, cancellationToken); if (HttpReceiveProgress != null && response != null && response.Content != null) { cancellationToken.ThrowIfCancellationRequested(); await AddResponseProgressAsync(request, response); } return response; } /// /// Raises the event. /// /// The request. /// The instance containing the event data. protected internal virtual void OnHttpRequestProgress(HttpRequestMessage request, HttpProgressEventArgs e) { if (HttpSendProgress != null) { HttpSendProgress(request, e); } } /// /// Raises the event. /// /// The request. /// The instance containing the event data. protected internal virtual void OnHttpResponseProgress(HttpRequestMessage request, HttpProgressEventArgs e) { if (HttpReceiveProgress != null) { HttpReceiveProgress(request, e); } } private void AddRequestProgress(HttpRequestMessage request) { if (HttpSendProgress != null && request != null && request.Content != null) { HttpContent progressContent = new ProgressContent(request.Content, this, request); request.Content = progressContent; } } private async Task AddResponseProgressAsync(HttpRequestMessage request, HttpResponseMessage response) { Stream stream = await response.Content.ReadAsStreamAsync(); ProgressStream progressStream = new ProgressStream(stream, this, request, response); HttpContent progressContent = new StreamContent(progressStream); response.Content.Headers.CopyTo(progressContent.Headers); response.Content = progressContent; return response; } } } ================================================ FILE: src/System.Net.Http.Formatting/Handlers/ProgressStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Internal; using System.Threading; using System.Threading.Tasks; namespace System.Net.Http.Handlers { /// /// This implementation of registers how much data has been /// read (received) versus written (sent) for a particular HTTP operation. The implementation /// is client side in that the total bytes to send is taken from the request and the total /// bytes to read is taken from the response. In a server side scenario, it would be the /// other way around (reading the request and writing the response). /// internal class ProgressStream : DelegatingStream { private readonly ProgressMessageHandler _handler; private readonly HttpRequestMessage _request; private long _bytesReceived; private long? _totalBytesToReceive; private long _bytesSent; private long? _totalBytesToSend; public ProgressStream(Stream innerStream, ProgressMessageHandler handler, HttpRequestMessage request, HttpResponseMessage response) : base(innerStream) { Contract.Assert(handler != null); Contract.Assert(request != null); if (request.Content != null) { _totalBytesToSend = request.Content.Headers.ContentLength; } if (response != null && response.Content != null) { _totalBytesToReceive = response.Content.Headers.ContentLength; } _handler = handler; _request = request; } public override int Read(byte[] buffer, int offset, int count) { int bytesRead = InnerStream.Read(buffer, offset, count); ReportBytesReceived(bytesRead, userState: null); return bytesRead; } public override int ReadByte() { int byteRead = InnerStream.ReadByte(); ReportBytesReceived(byteRead == -1 ? 0 : 1, userState: null); return byteRead; } public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { int readCount = await InnerStream.ReadAsync(buffer, offset, count, cancellationToken); ReportBytesReceived(readCount, userState: null); return readCount; } #if !NETSTANDARD1_3 // BeginX and EndX are not supported on Streams in netstandard1.3 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return InnerStream.BeginRead(buffer, offset, count, callback, state); } public override int EndRead(IAsyncResult asyncResult) { int bytesRead = InnerStream.EndRead(asyncResult); ReportBytesReceived(bytesRead, asyncResult.AsyncState); return bytesRead; } #endif public override void Write(byte[] buffer, int offset, int count) { InnerStream.Write(buffer, offset, count); ReportBytesSent(count, userState: null); } public override void WriteByte(byte value) { InnerStream.WriteByte(value); ReportBytesSent(1, userState: null); } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { await InnerStream.WriteAsync(buffer, offset, count, cancellationToken); ReportBytesSent(count, userState: null); } #if !NETSTANDARD1_3 // BeginX and EndX are not supported on Streams in netstandard1.3 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return new ProgressWriteAsyncResult(InnerStream, this, buffer, offset, count, callback, state); } public override void EndWrite(IAsyncResult asyncResult) { ProgressWriteAsyncResult.End(asyncResult); } #endif internal void ReportBytesSent(int bytesSent, object userState) { if (bytesSent > 0) { _bytesSent += bytesSent; int percentage = 0; if (_totalBytesToSend.HasValue && _totalBytesToSend != 0) { percentage = (int)((100L * _bytesSent) / _totalBytesToSend); } // We only pass the request as it is guaranteed to be non-null (the response may be null) _handler.OnHttpRequestProgress(_request, new HttpProgressEventArgs(percentage, userState, _bytesSent, _totalBytesToSend)); } } private void ReportBytesReceived(int bytesReceived, object userState) { if (bytesReceived > 0) { _bytesReceived += bytesReceived; int percentage = 0; if (_totalBytesToReceive.HasValue && _totalBytesToReceive != 0) { percentage = (int)((100L * _bytesReceived) / _totalBytesToReceive); } // We only pass the request as it is guaranteed to be non-null (the response may be null) _handler.OnHttpResponseProgress(_request, new HttpProgressEventArgs(percentage, userState, _bytesReceived, _totalBytesToReceive)); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Handlers/ProgressWriteAsyncResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Internal; namespace System.Net.Http.Handlers { internal class ProgressWriteAsyncResult : AsyncResult { private static readonly AsyncCallback _writeCompletedCallback = WriteCompletedCallback; private readonly Stream _innerStream; private readonly ProgressStream _progressStream; private readonly int _count; [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is handled as part of IAsyncResult completion.")] public ProgressWriteAsyncResult(Stream innerStream, ProgressStream progressStream, byte[] buffer, int offset, int count, AsyncCallback callback, object state) : base(callback, state) { Contract.Assert(innerStream != null); Contract.Assert(progressStream != null); Contract.Assert(buffer != null); _innerStream = innerStream; _progressStream = progressStream; _count = count; try { IAsyncResult result = innerStream.BeginWrite(buffer, offset, count, _writeCompletedCallback, this); if (result.CompletedSynchronously) { WriteCompleted(result); } } catch (Exception e) { Complete(true, e); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is handled as part of IAsyncResult completion.")] private static void WriteCompletedCallback(IAsyncResult result) { if (result.CompletedSynchronously) { return; } ProgressWriteAsyncResult thisPtr = (ProgressWriteAsyncResult)result.AsyncState; try { thisPtr.WriteCompleted(result); } catch (Exception e) { thisPtr.Complete(false, e); } } private void WriteCompleted(IAsyncResult result) { _innerStream.EndWrite(result); _progressStream.ReportBytesSent(_count, AsyncState); Complete(result.CompletedSynchronously); } public static void End(IAsyncResult result) { AsyncResult.End(result); } } } ================================================ FILE: src/System.Net.Http.Formatting/Headers/CookieHeaderValue.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Net.Http.Formatting; using System.Text; using System.Web.Http; namespace System.Net.Http.Headers { public class CookieHeaderValue : ICloneable { private const string ExpiresToken = "expires"; private const string MaxAgeToken = "max-age"; private const string DomainToken = "domain"; private const string PathToken = "path"; private const string SecureToken = "secure"; private const string HttpOnlyToken = "httponly"; private const string DefaultPath = "/"; private static readonly char[] segmentSeparator = new char[] { ';' }; private static readonly char[] nameValueSeparator = new char[] { '=' }; // Use list instead of dictionary since we may have multiple parameters with the same name. private Collection _cookies; public CookieHeaderValue(string name, string value) { CookieState cookie = new CookieState(name, value); Cookies.Add(cookie); } public CookieHeaderValue(string name, NameValueCollection values) { CookieState cookie = new CookieState(name, values); Cookies.Add(cookie); } /// /// Constructor to be used by parser to create a new instance of this type. /// protected CookieHeaderValue() { } private CookieHeaderValue(CookieHeaderValue source) { if (source == null) { throw Error.ArgumentNull("source"); } Expires = source.Expires; MaxAge = source.MaxAge; Domain = source.Domain; Path = source.Path; Secure = source.Secure; HttpOnly = source.HttpOnly; foreach (CookieState cookie in source.Cookies) { Cookies.Add(cookie.Clone()); } } public Collection Cookies { get { if (_cookies == null) { _cookies = new Collection(); } return _cookies; } } public DateTimeOffset? Expires { get; set; } public TimeSpan? MaxAge { get; set; } public string Domain { get; set; } public string Path { get; set; } public bool Secure { get; set; } public bool HttpOnly { get; set; } public CookieState this[string name] { get { if (String.IsNullOrEmpty(name)) { return null; } CookieState cookie = Cookies.FirstOrDefault(c => String.Equals(c.Name, name, StringComparison.OrdinalIgnoreCase)); if (cookie == null) { cookie = new CookieState(name, String.Empty); Cookies.Add(cookie); } return cookie; } } public override string ToString() { StringBuilder header = new StringBuilder(); bool first = true; foreach (CookieState cookie in Cookies) { first = AppendSegment(header, first, cookie.ToString(), null); } if (Expires.HasValue) { first = AppendSegment(header, first, ExpiresToken, FormattingUtilities.DateToString(Expires.Value)); } if (MaxAge.HasValue) { first = AppendSegment(header, first, MaxAgeToken, ((int)MaxAge.Value.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)); } if (Domain != null) { first = AppendSegment(header, first, DomainToken, Domain); } if (Path != null) { first = AppendSegment(header, first, PathToken, Path); } if (Secure) { first = AppendSegment(header, first, SecureToken, null); } if (HttpOnly) { first = AppendSegment(header, first, HttpOnlyToken, null); } return header.ToString(); } public object Clone() { return new CookieHeaderValue(this); } public static bool TryParse(string input, out CookieHeaderValue parsedValue) { parsedValue = null; if (!String.IsNullOrEmpty(input)) { string[] segments = input.Split(segmentSeparator); CookieHeaderValue instance = new CookieHeaderValue(); foreach (string segment in segments) { if (!ParseCookieSegment(instance, segment)) { return false; } } // If we didn't find any cookie state name/value pairs then cookie is not valid if (instance.Cookies.Count == 0) { return false; } parsedValue = instance; return true; } return false; } private static bool AppendSegment(StringBuilder builder, bool first, string name, string value) { if (first) { first = false; } else { builder.Append("; "); } builder.Append(name); if (value != null) { builder.Append("="); builder.Append(value); } return first; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is a try method where we do want to ignore errors.")] private static bool ParseCookieSegment(CookieHeaderValue instance, string segment) { if (String.IsNullOrWhiteSpace(segment)) { return true; } string[] nameValue = segment.Split(nameValueSeparator, 2); if (nameValue.Length < 1 || String.IsNullOrWhiteSpace(nameValue[0])) { return false; } string name = nameValue[0].Trim(); if (String.Equals(name, ExpiresToken, StringComparison.OrdinalIgnoreCase)) { string value = GetSegmentValue(nameValue, null); DateTimeOffset expires; if (FormattingUtilities.TryParseDate(value, out expires)) { instance.Expires = expires; return true; } return false; } else if (String.Equals(name, MaxAgeToken, StringComparison.OrdinalIgnoreCase)) { string value = GetSegmentValue(nameValue, null); int maxAge; if (FormattingUtilities.TryParseInt32(value, out maxAge)) { instance.MaxAge = new TimeSpan(0, 0, maxAge); return true; } return false; } else if (String.Equals(name, DomainToken, StringComparison.OrdinalIgnoreCase)) { instance.Domain = GetSegmentValue(nameValue, null); return true; } else if (String.Equals(name, PathToken, StringComparison.OrdinalIgnoreCase)) { instance.Path = GetSegmentValue(nameValue, DefaultPath); return true; } else if (String.Equals(name, SecureToken, StringComparison.OrdinalIgnoreCase)) { string value = GetSegmentValue(nameValue, null); if (!String.IsNullOrWhiteSpace(value)) { return false; } instance.Secure = true; return true; } else if (String.Equals(name, HttpOnlyToken, StringComparison.OrdinalIgnoreCase)) { string value = GetSegmentValue(nameValue, null); if (!String.IsNullOrWhiteSpace(value)) { return false; } instance.HttpOnly = true; return true; } else { string value = GetSegmentValue(nameValue, null); // We read the cookie segment as form data try { FormDataCollection formData = new FormDataCollection(value); NameValueCollection values = formData.ReadAsNameValueCollection(); CookieState cookie = new CookieState(name, values); instance.Cookies.Add(cookie); return true; } catch { return false; } } } private static string GetSegmentValue(string[] nameValuePair, string defaultValue) { Contract.Assert(nameValuePair != null); return nameValuePair.Length > 1 ? FormattingUtilities.UnquoteToken(nameValuePair[1]) : defaultValue; } } } ================================================ FILE: src/System.Net.Http.Formatting/Headers/CookieState.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Diagnostics.Contracts; using System.Net.Http.Formatting.Internal; using System.Web.Http; namespace System.Net.Http.Headers { public class CookieState : ICloneable { private string _name; private NameValueCollection _values = HttpValueCollection.Create(); public CookieState(string name) : this(name, String.Empty) { } public CookieState(string name, string value) { CheckNameFormat(name, "name"); _name = name; CheckValueFormat(value, "value"); Value = value; } public CookieState(string name, NameValueCollection values) { CheckNameFormat(name, "name"); _name = name; if (values == null) { throw Error.ArgumentNull("values"); } Values.Add(values); } private CookieState(CookieState source) { Contract.Requires(source != null); _name = source._name; if (source._values != null) { Values.Add(source._values); } } public string Name { get { return _name; } set { CheckNameFormat(value, "value"); _name = value; } } /// /// If the cookie data is a simple string value then set or retrieve it using the property. /// If the cookie data is structured then use the property. /// public string Value { get { return Values.Count > 0 ? Values.AllKeys[0] : String.Empty; } set { CheckValueFormat(value, "value"); if (Values.Count > 0) { Values.AllKeys[0] = value; } else { Values.Add(value, String.Empty); } } } /// /// If the cookie data is structured then use the property for setting and getting individual sub-name/value pairs. /// If the cookie data is a simple string value then set or retrieve it using the property. /// public NameValueCollection Values { get { return _values; } } public string this[string subName] { get { return Values[subName]; } set { Values[subName] = value; } } public override string ToString() { return _name + "=" + (_values != null ? _values.ToString() : String.Empty); } public object Clone() { return new CookieState(this); } private static void CheckNameFormat(string name, string parameterName) { if (name == null) { throw Error.ArgumentNull("name"); } if (!FormattingUtilities.ValidateHeaderToken(name)) { throw Error.Argument(parameterName, Properties.Resources.CookieInvalidName); } } private static void CheckValueFormat(string value, string parameterName) { // Empty string is a valid cookie value if (value == null) { throw Error.ArgumentNull(parameterName); } } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpClientExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Extension methods that aid in making formatted requests using . /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpClientExtensions { /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsJsonAsync(this HttpClient client, string requestUri, T value) { return client.PostAsJsonAsync(requestUri, value, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsJsonAsync(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, new JsonMediaTypeFormatter(), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. public static Task PostAsJsonAsync(this HttpClient client, Uri requestUri, T value) { return client.PostAsJsonAsync(requestUri, value, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PostAsJsonAsync(this HttpClient client, Uri requestUri, T value, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, new JsonMediaTypeFormatter(), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsXmlAsync(this HttpClient client, string requestUri, T value) { return client.PostAsXmlAsync(requestUri, value, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsXmlAsync(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, new XmlMediaTypeFormatter(), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. public static Task PostAsXmlAsync(this HttpClient client, Uri requestUri, T value) { return client.PostAsXmlAsync(requestUri, value, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PostAsXmlAsync(this HttpClient client, Uri requestUri, T value, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, new XmlMediaTypeFormatter(), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter) { return client.PostAsync(requestUri, value, formatter, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, formatter, mediaType: (MediaTypeHeaderValue)null, cancellationToken: cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, string mediaType) { return client.PostAsync(requestUri, value, formatter, mediaType, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PostAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, string mediaType, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, formatter, ObjectContent.BuildHeaderValue(mediaType), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The caller is responsible for disposing the object")] [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The called method will convert to Uri instance.")] public static Task PostAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken) { if (client == null) { throw Error.ArgumentNull("client"); } var content = new ObjectContent(value, formatter, mediaType); return client.PostAsync(requestUri, content, cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// A task object representing the asynchronous operation. public static Task PostAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter) { return client.PostAsync(requestUri, value, formatter, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PostAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, formatter, mediaType: (MediaTypeHeaderValue)null, cancellationToken: cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// A task object representing the asynchronous operation. public static Task PostAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, string mediaType) { return client.PostAsync(requestUri, value, formatter, mediaType, CancellationToken.None); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PostAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, string mediaType, CancellationToken cancellationToken) { return client.PostAsync(requestUri, value, formatter, ObjectContent.BuildHeaderValue(mediaType), cancellationToken); } /// /// Sends a POST request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The caller is responsible for disposing the object")] public static Task PostAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken) { if (client == null) { throw Error.ArgumentNull("client"); } var content = new ObjectContent(value, formatter, mediaType); return client.PostAsync(requestUri, content, cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsJsonAsync(this HttpClient client, string requestUri, T value) { return client.PutAsJsonAsync(requestUri, value, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsJsonAsync(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, new JsonMediaTypeFormatter(), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. public static Task PutAsJsonAsync(this HttpClient client, Uri requestUri, T value) { return client.PutAsJsonAsync(requestUri, value, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as JSON. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PutAsJsonAsync(this HttpClient client, Uri requestUri, T value, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, new JsonMediaTypeFormatter(), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses a default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsXmlAsync(this HttpClient client, string requestUri, T value) { return client.PutAsXmlAsync(requestUri, value, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsXmlAsync(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, new XmlMediaTypeFormatter(), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// A task object representing the asynchronous operation. public static Task PutAsXmlAsync(this HttpClient client, Uri requestUri, T value) { return client.PutAsXmlAsync(requestUri, value, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with the given serialized /// as XML. /// /// /// This method uses the default instance of . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PutAsXmlAsync(this HttpClient client, Uri requestUri, T value, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, new XmlMediaTypeFormatter(), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter) { return client.PutAsync(requestUri, value, formatter, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, formatter, mediaType: (MediaTypeHeaderValue)null, cancellationToken: cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, string mediaType) { return client.PutAsync(requestUri, value, formatter, mediaType, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] public static Task PutAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, string mediaType, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, formatter, ObjectContent.BuildHeaderValue(mediaType), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to support URIs as strings")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The caller is responsible for disposing the object")] [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The called method will convert to Uri instance.")] public static Task PutAsync(this HttpClient client, string requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken) { if (client == null) { throw Error.ArgumentNull("client"); } var content = new ObjectContent(value, formatter, mediaType); return client.PutAsync(requestUri, content, cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// A task object representing the asynchronous operation. public static Task PutAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter) { return client.PutAsync(requestUri, value, formatter, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PutAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, formatter, mediaType: (MediaTypeHeaderValue)null, cancellationToken: cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// A task object representing the asynchronous operation. public static Task PutAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, string mediaType) { return client.PutAsync(requestUri, value, formatter, mediaType, CancellationToken.None); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. public static Task PutAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, string mediaType, CancellationToken cancellationToken) { return client.PutAsync(requestUri, value, formatter, ObjectContent.BuildHeaderValue(mediaType), cancellationToken); } /// /// Sends a PUT request as an asynchronous operation to the specified Uri with /// serialized using the given . /// /// The type of . /// The client used to make the request. /// The Uri the request is sent to. /// The value that will be placed in the request's entity body. /// The formatter used to serialize the . /// The authoritative value of the request's content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. /// The token to monitor for cancellation requests. /// A task object representing the asynchronous operation. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The caller is responsible for disposing the object")] public static Task PutAsync(this HttpClient client, Uri requestUri, T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, CancellationToken cancellationToken) { if (client == null) { throw Error.ArgumentNull("client"); } var content = new ObjectContent(value, formatter, mediaType); return client.PutAsync(requestUri, content, cancellationToken); } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpClientFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Http; namespace System.Net.Http { public static class HttpClientFactory { /// /// Creates a new instance configured with the handlers provided and with an /// as the innermost handler. /// /// An ordered list of instances to be invoked as an /// travels from the to the network and an /// travels from the network back to . /// The handlers are invoked in a top-down fashion. That is, the first entry is invoked first for /// an outbound request message but last for an inbound response message. /// An instance with the configured handlers. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Handler is disposed with HttpClient")] public static HttpClient Create(params DelegatingHandler[] handlers) { return Create(new HttpClientHandler(), handlers); } /// /// Creates a new instance configured with the handlers provided and with the /// provided as the innermost handler. /// /// The inner handler represents the destination of the HTTP message channel. /// An ordered list of instances to be invoked as an /// travels from the to the network and an /// travels from the network back to . /// The handlers are invoked in a top-down fashion. That is, the first entry is invoked first for /// an outbound request message but last for an inbound response message. /// An instance with the configured handlers. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Handler is disposed with HttpClient")] public static HttpClient Create(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers) { HttpMessageHandler pipeline = CreatePipeline(innerHandler, handlers); return new HttpClient(pipeline); } /// /// Creates an instance of an using the instances /// provided by . The resulting pipeline can be used to manually create /// or instances with customized message handlers. /// /// The inner handler represents the destination of the HTTP message channel. /// An ordered list of instances to be invoked as part /// of sending an and receiving an . /// The handlers are invoked in a top-down fashion. That is, the first entry is invoked first for /// an outbound request message but last for an inbound response message. /// The HTTP message channel. public static HttpMessageHandler CreatePipeline(HttpMessageHandler innerHandler, IEnumerable handlers) { if (innerHandler == null) { throw Error.ArgumentNull("innerHandler"); } if (handlers == null) { return innerHandler; } // Wire handlers up in reverse order starting with the inner handler HttpMessageHandler pipeline = innerHandler; IEnumerable reversedHandlers = handlers.Reverse(); foreach (DelegatingHandler handler in reversedHandlers) { if (handler == null) { throw Error.Argument("handlers", Properties.Resources.DelegatingHandlerArrayContainsNullItem, typeof(DelegatingHandler).Name); } if (handler.InnerHandler != null) { throw Error.Argument("handlers", Properties.Resources.DelegatingHandlerArrayHasNonNullInnerHandler, typeof(DelegatingHandler).Name, "InnerHandler", handler.GetType().Name); } handler.InnerHandler = pipeline; pipeline = handler; } return pipeline; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpContentExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Extension methods to allow strongly typed objects to be read from instances. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpContentExtensions { private static MediaTypeFormatterCollection _defaultMediaTypeFormatterCollection = null; // Using the JsonMediaTypeFormatter for the first time is rather expensive (due to reflection cost // when creating the default contract resolver). Hence we new up a static collection, such // that the second call is much faster. private static MediaTypeFormatterCollection DefaultMediaTypeFormatterCollection { get { if (_defaultMediaTypeFormatterCollection == null) { _defaultMediaTypeFormatterCollection = new MediaTypeFormatterCollection(); } return _defaultMediaTypeFormatterCollection; } } /// /// Returns a that will yield an object of the specified /// from the instance. /// /// This override use the built-in collection of formatters. /// The instance from which to read. /// The type of the object to read. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type) { return content.ReadAsAsync(type, DefaultMediaTypeFormatterCollection); } /// /// Returns a that will yield an object of the specified /// from the instance. /// /// This override use the built-in collection of formatters. /// The instance from which to read. /// The type of the object to read. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type, CancellationToken cancellationToken) { return content.ReadAsAsync(type, DefaultMediaTypeFormatterCollection, cancellationToken); } /// /// Returns a that will yield an object of the specified /// from the instance using one of the provided /// to deserialize the content. /// /// The instance from which to read. /// The type of the object to read. /// The collection of instances to use. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters) { return ReadAsAsync(content, type, formatters, null); } /// /// Returns a that will yield an object of the specified /// from the instance using one of the provided /// to deserialize the content. /// /// The instance from which to read. /// The type of the object to read. /// The collection of instances to use. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters, CancellationToken cancellationToken) { return ReadAsAsync(content, type, formatters, null, cancellationToken); } /// /// Returns a that will yield an object of the specified /// from the instance using one of the provided /// to deserialize the content. /// /// The instance from which to read. /// The type of the object to read. /// The collection of instances to use. /// The to log events to. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger) { return ReadAsAsync(content, type, formatters, formatterLogger); } /// /// Returns a that will yield an object of the specified /// from the instance using one of the provided /// to deserialize the content. /// /// The instance from which to read. /// The type of the object to read. /// The collection of instances to use. /// The to log events to. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { return ReadAsAsync(content, type, formatters, formatterLogger, cancellationToken); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// This override use the built-in collection of formatters. /// The type of the object to read. /// The instance from which to read. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content) { return content.ReadAsAsync(DefaultMediaTypeFormatterCollection); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// This override use the built-in collection of formatters. /// The type of the object to read. /// The instance from which to read. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, CancellationToken cancellationToken) { return content.ReadAsAsync(DefaultMediaTypeFormatterCollection, cancellationToken); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// The type of the object to read. /// The instance from which to read. /// The collection of instances to use. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters) { return ReadAsAsync(content, typeof(T), formatters, null); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// The type of the object to read. /// The instance from which to read. /// The collection of instances to use. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters, CancellationToken cancellationToken) { return ReadAsAsync(content, typeof(T), formatters, null, cancellationToken); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// The type of the object to read. /// The instance from which to read. /// The collection of instances to use. /// The to log events to. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters, IFormatterLogger formatterLogger) { return ReadAsAsync(content, typeof(T), formatters, formatterLogger); } /// /// Returns a that will yield an object of the specified /// type from the instance. /// /// The type of the object to read. /// The instance from which to read. /// The collection of instances to use. /// The to log events to. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an object of the specified type. public static Task ReadAsAsync(this HttpContent content, IEnumerable formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { return ReadAsAsync(content, typeof(T), formatters, formatterLogger, cancellationToken); } private static Task ReadAsAsync(HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger) { return ReadAsAsync(content, type, formatters, formatterLogger, CancellationToken.None); } // There are many helper overloads for ReadAs*(). Provide one worker function to ensure the logic is shared. // // For loosely typed, T = Object, type = specific class. // For strongly typed, T == type.GetType() private static Task ReadAsAsync(HttpContent content, Type type, IEnumerable formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { if (content == null) { throw Error.ArgumentNull("content"); } if (type == null) { throw Error.ArgumentNull("type"); } if (formatters == null) { throw Error.ArgumentNull("formatters"); } ObjectContent objectContent = content as ObjectContent; if (objectContent != null && objectContent.Value != null && type.IsAssignableFrom(objectContent.Value.GetType())) { return Task.FromResult((T)objectContent.Value); } MediaTypeFormatter formatter = null; // Default to "application/octet-stream" if there is no content-type in accordance with section 7.2.1 of the HTTP spec MediaTypeHeaderValue mediaType = content.Headers.ContentType ?? MediaTypeConstants.ApplicationOctetStreamMediaType; formatter = new MediaTypeFormatterCollection(formatters).FindReader(type, mediaType); if (formatter == null) { if (content.Headers.ContentLength == 0) { T defaultValue = (T)MediaTypeFormatter.GetDefaultValueForType(type); return Task.FromResult(defaultValue); } throw new UnsupportedMediaTypeException( Error.Format(Properties.Resources.NoReadSerializerAvailable, type.Name, mediaType.MediaType), mediaType); } return ReadAsAsyncCore(content, type, formatterLogger, formatter, cancellationToken); } private static async Task ReadAsAsyncCore(HttpContent content, Type type, IFormatterLogger formatterLogger, MediaTypeFormatter formatter, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Stream stream = await content.ReadAsStreamAsync(); object result = await formatter.ReadFromStreamAsync(type, stream, content, formatterLogger, cancellationToken); return (T)result; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpContentFormDataExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.ComponentModel; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Extension methods to allow HTML form URL-encoded data, also known as application/x-www-form-urlencoded, /// to be read from instances. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpContentFormDataExtensions { private const string ApplicationFormUrlEncoded = "application/x-www-form-urlencoded"; /// /// Determines whether the specified content is HTML form URL-encoded data, also known as application/x-www-form-urlencoded data. /// /// The content. /// /// true if the specified content is HTML form URL-encoded data; otherwise, false. /// public static bool IsFormData(this HttpContent content) { if (content == null) { throw Error.ArgumentNull("content"); } MediaTypeHeaderValue contentType = content.Headers.ContentType; return contentType != null && String.Equals(ApplicationFormUrlEncoded, contentType.MediaType, StringComparison.OrdinalIgnoreCase); } /// /// Returns a that will yield a instance containing the form data /// parsed as HTML form URL-encoded from the instance. /// /// The content. /// A which will provide the result. If the data can not be read /// as HTML form URL-encoded data then the result is null. public static Task ReadAsFormDataAsync(this HttpContent content) { return ReadAsFormDataAsync(content, CancellationToken.None); } /// /// Returns a that will yield a instance containing the form data /// parsed as HTML form URL-encoded from the instance. /// /// The content. /// The token to monitor for cancellation requests. /// A which will provide the result. If the data can not be read /// as HTML form URL-encoded data then the result is null. public static Task ReadAsFormDataAsync(this HttpContent content, CancellationToken cancellationToken) { if (content == null) { throw Error.ArgumentNull("content"); } MediaTypeFormatter[] formatters = new MediaTypeFormatter[1] { new FormUrlEncodedMediaTypeFormatter() }; return ReadAsAsyncCore(content, formatters, cancellationToken); } private static async Task ReadAsAsyncCore(HttpContent content, MediaTypeFormatter[] formatters, CancellationToken cancellationToken) { FormDataCollection formData = await content.ReadAsAsync(formatters, cancellationToken); return formData == null ? null : formData.ReadAsNameValueCollection(); } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpContentMessageExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Extension methods to read and entities from instances. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpContentMessageExtensions { private const int MinBufferSize = 256; private const int DefaultBufferSize = 32 * 1024; /// /// Determines whether the specified content is HTTP request message content. /// /// The content. /// /// true if the specified content is HTTP message content; otherwise, false. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to false.")] public static bool IsHttpRequestMessageContent(this HttpContent content) { if (content == null) { throw Error.ArgumentNull("content"); } try { return HttpMessageContent.ValidateHttpMessageContent(content, true, false); } catch (Exception) { return false; } } /// /// Determines whether the specified content is HTTP response message content. /// /// The content. /// /// true if the specified content is HTTP message content; otherwise, false. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to false.")] public static bool IsHttpResponseMessageContent(this HttpContent content) { if (content == null) { throw Error.ArgumentNull("content"); } try { return HttpMessageContent.ValidateHttpMessageContent(content, false, false); } catch (Exception) { return false; } } /// /// Read the as an . /// /// The content to read. /// A task object representing reading the content as an . public static Task ReadAsHttpRequestMessageAsync(this HttpContent content) { return ReadAsHttpRequestMessageAsync(content, "http", DefaultBufferSize); } /// /// Read the as an . /// /// The content to read. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, CancellationToken cancellationToken) { return ReadAsHttpRequestMessageAsync(content, "http", DefaultBufferSize, cancellationToken); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme) { return ReadAsHttpRequestMessageAsync(content, uriScheme, DefaultBufferSize); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, CancellationToken cancellationToken) { return ReadAsHttpRequestMessageAsync(content, uriScheme, DefaultBufferSize, cancellationToken); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI (the /// URI scheme is not actually part of the HTTP Request URI and so must be provided externally). /// Size of the buffer. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize) { return ReadAsHttpRequestMessageAsync(content, uriScheme, bufferSize, HttpRequestHeaderParser.DefaultMaxHeaderSize); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI (the /// URI scheme is not actually part of the HTTP Request URI and so must be provided externally). /// Size of the buffer. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize, CancellationToken cancellationToken) { return ReadAsHttpRequestMessageAsync(content, uriScheme, bufferSize, HttpRequestHeaderParser.DefaultMaxHeaderSize, cancellationToken); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI (the /// URI scheme is not actually part of the HTTP Request URI and so must be provided externally). /// Size of the buffer. /// The max length of the HTTP header. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to parser state.")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize, int maxHeaderSize) { return ReadAsHttpRequestMessageAsync(content, uriScheme, bufferSize, maxHeaderSize, CancellationToken.None); } /// /// Read the as an . /// /// The content to read. /// The URI scheme to use for the request URI (the /// URI scheme is not actually part of the HTTP Request URI and so must be provided externally). /// Size of the buffer. /// The max length of the HTTP header. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a full URI but only the URI scheme")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to parser state.")] public static Task ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize, int maxHeaderSize, CancellationToken cancellationToken) { if (content == null) { throw Error.ArgumentNull("content"); } if (uriScheme == null) { throw Error.ArgumentNull("uriScheme"); } if (!Uri.CheckSchemeName(uriScheme)) { throw Error.Argument("uriScheme", Properties.Resources.HttpMessageParserInvalidUriScheme, uriScheme, typeof(Uri).Name); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } if (maxHeaderSize < InternetMessageFormatHeaderParser.MinHeaderSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxHeaderSize", maxHeaderSize, InternetMessageFormatHeaderParser.MinHeaderSize); } HttpMessageContent.ValidateHttpMessageContent(content, true, true); return content.ReadAsHttpRequestMessageAsyncCore(uriScheme, bufferSize, maxHeaderSize, cancellationToken); } private static async Task ReadAsHttpRequestMessageAsyncCore(this HttpContent content, string uriScheme, int bufferSize, int maxHeaderSize, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Stream stream = await content.ReadAsStreamAsync(); HttpUnsortedRequest httpRequest = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(httpRequest, Math.Max(HttpRequestHeaderParser.DefaultMaxRequestLineSize, maxHeaderSize), maxHeaderSize); ParserState parseStatus; byte[] buffer = new byte[bufferSize]; int bytesRead = 0; int headerConsumed = 0; while (true) { try { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); } catch (Exception e) { throw new IOException(Properties.Resources.HttpMessageErrorReading, e); } try { parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed); } catch (Exception) { parseStatus = ParserState.Invalid; } if (parseStatus == ParserState.Done) { return CreateHttpRequestMessage(uriScheme, httpRequest, stream, bytesRead - headerConsumed); } else if (parseStatus != ParserState.NeedMoreData) { throw Error.InvalidOperation(Properties.Resources.HttpMessageParserError, headerConsumed, buffer); } else if (bytesRead == 0) { throw new IOException(Properties.Resources.ReadAsHttpMessageUnexpectedTermination); } } } /// /// Read the as an . /// /// The content to read. /// A task object representing reading the content as an . public static Task ReadAsHttpResponseMessageAsync(this HttpContent content) { return ReadAsHttpResponseMessageAsync(content, DefaultBufferSize); } /// /// Read the as an . /// /// The content to read. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . public static Task ReadAsHttpResponseMessageAsync(this HttpContent content, CancellationToken cancellationToken) { return ReadAsHttpResponseMessageAsync(content, DefaultBufferSize, cancellationToken); } /// /// Read the as an . /// /// The content to read. /// Size of the buffer. /// A task object representing reading the content as an . public static Task ReadAsHttpResponseMessageAsync(this HttpContent content, int bufferSize) { return ReadAsHttpResponseMessageAsync(content, bufferSize, HttpResponseHeaderParser.DefaultMaxHeaderSize); } /// /// Read the as an . /// /// The content to read. /// Size of the buffer. /// The token to monitor for cancellation requests. /// A task object representing reading the content as an . public static Task ReadAsHttpResponseMessageAsync(this HttpContent content, int bufferSize, CancellationToken cancellationToken) { return ReadAsHttpResponseMessageAsync(content, bufferSize, HttpResponseHeaderParser.DefaultMaxHeaderSize, cancellationToken); } /// /// Read the as an . /// /// The content to read. /// Size of the buffer. /// The max length of the HTTP header. /// A task object representing reading the content as an . [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to parser state.")] public static Task ReadAsHttpResponseMessageAsync(this HttpContent content, int bufferSize, int maxHeaderSize) { return ReadAsHttpResponseMessageAsync(content, bufferSize, maxHeaderSize, CancellationToken.None); } /// /// Read the as an . /// /// The content to read. /// Size of the buffer. /// The max length of the HTTP header. /// The token to monitor for cancellation requests. /// The parsed instance. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception translates to parser state.")] public static Task ReadAsHttpResponseMessageAsync(this HttpContent content, int bufferSize, int maxHeaderSize, CancellationToken cancellationToken) { if (content == null) { throw Error.ArgumentNull("content"); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } if (maxHeaderSize < InternetMessageFormatHeaderParser.MinHeaderSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxHeaderSize", maxHeaderSize, InternetMessageFormatHeaderParser.MinHeaderSize); } HttpMessageContent.ValidateHttpMessageContent(content, false, true); return content.ReadAsHttpResponseMessageAsyncCore(bufferSize, maxHeaderSize, cancellationToken); } private static async Task ReadAsHttpResponseMessageAsyncCore(this HttpContent content, int bufferSize, int maxHeaderSize, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Stream stream = await content.ReadAsStreamAsync(); HttpUnsortedResponse httpResponse = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(httpResponse, HttpResponseHeaderParser.DefaultMaxStatusLineSize, maxHeaderSize); ParserState parseStatus; byte[] buffer = new byte[bufferSize]; int bytesRead = 0; int headerConsumed = 0; while (true) { try { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); } catch (Exception e) { throw new IOException(Properties.Resources.HttpMessageErrorReading, e); } try { parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed); } catch (Exception) { parseStatus = ParserState.Invalid; } if (parseStatus == ParserState.Done) { // Create and return parsed HttpResponseMessage return CreateHttpResponseMessage(httpResponse, stream, bytesRead - headerConsumed); } else if (parseStatus != ParserState.NeedMoreData) { throw Error.InvalidOperation(Properties.Resources.HttpMessageParserError, headerConsumed, buffer); } else if (bytesRead == 0) { throw new IOException(Properties.Resources.ReadAsHttpMessageUnexpectedTermination); } } } /// /// Creates the request URI by combining scheme (provided) with parsed values of /// host and path. /// /// The URI scheme to use for the request URI. /// The unsorted HTTP request. /// A fully qualified request URI. private static Uri CreateRequestUri(string uriScheme, HttpUnsortedRequest httpRequest) { Contract.Assert(httpRequest != null, "httpRequest cannot be null."); Contract.Assert(uriScheme != null, "uriScheme cannot be null"); IEnumerable hostValues; if (httpRequest.HttpHeaders.TryGetValues(FormattingUtilities.HttpHostHeader, out hostValues)) { int hostCount = hostValues.Count(); if (hostCount != 1) { throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, hostCount); } } else { throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, 0); } // We don't use UriBuilder as hostValues.ElementAt(0) contains 'host:port' and UriBuilder needs these split out into separate host and port. string requestUri = String.Format(CultureInfo.InvariantCulture, "{0}://{1}{2}", uriScheme, hostValues.ElementAt(0), httpRequest.RequestUri); return new Uri(requestUri); } /// /// Copies the unsorted header fields to a sorted collection. /// /// The unsorted source headers /// The destination or . /// The input used to form any being part of this HTTP request. /// Start location of any request entity within the . /// An instance if header fields contained and . private static HttpContent CreateHeaderFields(HttpHeaders source, HttpHeaders destination, Stream contentStream, int rewind) { Contract.Assert(source != null, "source headers cannot be null"); Contract.Assert(destination != null, "destination headers cannot be null"); Contract.Assert(contentStream != null, "contentStream must be non null"); HttpContentHeaders contentHeaders = null; HttpContent content; // Set the header fields foreach (KeyValuePair> header in source) { if (!destination.TryAddWithoutValidation(header.Key, header.Value)) { if (contentHeaders == null) { contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); } contentHeaders.TryAddWithoutValidation(header.Key, header.Value); } } // If we have content headers then create an HttpContent for this request or response. Otherwise, // provide a HttpContent instance to overwrite the null or EmptyContent value. if (contentHeaders != null) { // Need to rewind the input stream to be at the position right after the HTTP header // which we may already have parsed as we read the content stream. if (!contentStream.CanSeek) { throw Error.InvalidOperation(Properties.Resources.HttpMessageContentStreamMustBeSeekable, "ContentReadStream", FormattingUtilities.HttpResponseMessageType.Name); } contentStream.Seek(0 - rewind, SeekOrigin.Current); content = new StreamContent(contentStream); contentHeaders.CopyTo(content.Headers); } else { content = new StreamContent(Stream.Null); } return content; } /// /// Creates an based on information provided in . /// /// The URI scheme to use for the request URI. /// The unsorted HTTP request. /// The input used to form any being part of this HTTP request. /// Start location of any request entity within the . /// A newly created instance. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")] private static HttpRequestMessage CreateHttpRequestMessage(string uriScheme, HttpUnsortedRequest httpRequest, Stream contentStream, int rewind) { Contract.Assert(uriScheme != null, "URI scheme must be non null"); Contract.Assert(httpRequest != null, "httpRequest must be non null"); Contract.Assert(contentStream != null, "contentStream must be non null"); HttpRequestMessage httpRequestMessage = new HttpRequestMessage(); // Set method, requestURI, and version httpRequestMessage.Method = httpRequest.Method; httpRequestMessage.RequestUri = CreateRequestUri(uriScheme, httpRequest); httpRequestMessage.Version = httpRequest.Version; // Set the header fields and content if any httpRequestMessage.Content = CreateHeaderFields(httpRequest.HttpHeaders, httpRequestMessage.Headers, contentStream, rewind); return httpRequestMessage; } /// /// Creates an based on information provided in . /// /// The unsorted HTTP Response. /// The input used to form any being part of this HTTP Response. /// Start location of any Response entity within the . /// A newly created instance. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")] private static HttpResponseMessage CreateHttpResponseMessage(HttpUnsortedResponse httpResponse, Stream contentStream, int rewind) { Contract.Assert(httpResponse != null, "httpResponse must be non null"); Contract.Assert(contentStream != null, "contentStream must be non null"); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); // Set version, status code and reason phrase httpResponseMessage.Version = httpResponse.Version; httpResponseMessage.StatusCode = httpResponse.StatusCode; httpResponseMessage.ReasonPhrase = httpResponse.ReasonPhrase; // Set the header fields and content if any httpResponseMessage.Content = CreateHeaderFields(httpResponse.HttpHeaders, httpResponseMessage.Headers, contentStream, rewind); return httpResponseMessage; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpContentMultipartExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting.Parsers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Extension methods to read MIME multipart entities from instances. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpContentMultipartExtensions { private const int MinBufferSize = 256; private const int DefaultBufferSize = 32 * 1024; /// /// Determines whether the specified content is MIME multipart content. /// /// The content. /// /// true if the specified content is MIME multipart content; otherwise, false. /// public static bool IsMimeMultipartContent(this HttpContent content) { if (content == null) { throw Error.ArgumentNull("content"); } return MimeMultipartBodyPartParser.IsMimeMultipartContent(content); } /// /// Determines whether the specified content is MIME multipart content with the /// specified subtype. For example, the subtype mixed would match content /// with a content type of multipart/mixed. /// /// The content. /// The MIME multipart subtype to match. /// /// true if the specified content is MIME multipart content with the specified subtype; otherwise, false. /// public static bool IsMimeMultipartContent(this HttpContent content, string subtype) { if (String.IsNullOrWhiteSpace(subtype)) { throw Error.ArgumentNull("subtype"); } if (IsMimeMultipartContent(content)) { if (content.Headers.ContentType.MediaType.Equals("multipart/" + subtype, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } /// /// Reads all body parts within a MIME multipart message into memory using a . /// /// An existing instance to use for the object's content. /// A representing the tasks of getting the result of reading the MIME content. public static Task ReadAsMultipartAsync(this HttpContent content) { return ReadAsMultipartAsync(content, new MultipartMemoryStreamProvider(), DefaultBufferSize); } /// /// Reads all body parts within a MIME multipart message into memory using a . /// /// An existing instance to use for the object's content. /// The token to monitor for cancellation requests. /// A representing the tasks of getting the result of reading the MIME content. public static Task ReadAsMultipartAsync(this HttpContent content, CancellationToken cancellationToken) { return ReadAsMultipartAsync(content, new MultipartMemoryStreamProvider(), DefaultBufferSize, cancellationToken); } /// /// Reads all body parts within a MIME multipart message using the provided instance /// to determine where the contents of each body part is written. /// /// The with which to process the data. /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. /// A representing the tasks of getting the result of reading the MIME content. public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider) where T : MultipartStreamProvider { return ReadAsMultipartAsync(content, streamProvider, DefaultBufferSize); } /// /// Reads all body parts within a MIME multipart message using the provided instance /// to determine where the contents of each body part is written. /// /// The with which to process the data. /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. /// The token to monitor for cancellation requests. /// A representing the tasks of getting the result of reading the MIME content. public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, CancellationToken cancellationToken) where T : MultipartStreamProvider { return ReadAsMultipartAsync(content, streamProvider, DefaultBufferSize, cancellationToken); } /// /// Reads all body parts within a MIME multipart message using the provided instance /// to determine where the contents of each body part is written and as read buffer size. /// /// The with which to process the data. /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. /// Size of the buffer used to read the contents. /// A representing the tasks of getting the result of reading the MIME content. public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, int bufferSize) where T : MultipartStreamProvider { return ReadAsMultipartAsync(content, streamProvider, bufferSize, CancellationToken.None); } /// /// Reads all body parts within a MIME multipart message using the provided instance /// to determine where the contents of each body part is written and as read buffer size. /// /// The with which to process the data. /// An existing instance to use for the object's content. /// A stream provider providing output streams for where to write body parts as they are parsed. /// Size of the buffer used to read the contents. /// The token to monitor for cancellation requests. /// A representing the tasks of getting the result of reading the MIME content. public static async Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, int bufferSize, CancellationToken cancellationToken) where T : MultipartStreamProvider { if (content == null) { throw Error.ArgumentNull("content"); } if (streamProvider == null) { throw Error.ArgumentNull("streamProvider"); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } Stream stream; try { stream = await content.ReadAsStreamAsync(); } catch (Exception e) { throw new IOException(Properties.Resources.ReadAsMimeMultipartErrorReading, e); } using (var parser = new MimeMultipartBodyPartParser(content, streamProvider)) { byte[] data = new byte[bufferSize]; MultipartAsyncContext context = new MultipartAsyncContext(stream, parser, data, streamProvider.Contents); // Start async read/write loop await MultipartReadAsync(context, cancellationToken); // Let the stream provider post-process when everything is complete await streamProvider.ExecutePostProcessingAsync(cancellationToken); return streamProvider; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")] private static async Task MultipartReadAsync(MultipartAsyncContext context, CancellationToken cancellationToken) { Contract.Assert(context != null, "context cannot be null"); while (true) { int bytesRead; try { bytesRead = await context.ContentStream.ReadAsync(context.Data, 0, context.Data.Length, cancellationToken); } catch (Exception e) { throw new IOException(Properties.Resources.ReadAsMimeMultipartErrorReading, e); } IEnumerable parts = context.MimeParser.ParseBuffer(context.Data, bytesRead); foreach (MimeBodyPart part in parts) { foreach (ArraySegment segment in part.Segments) { try { await part.WriteSegment(segment, cancellationToken); } catch (Exception e) { part.Dispose(); throw new IOException(Properties.Resources.ReadAsMimeMultipartErrorWriting, e); } } if (CheckIsFinalPart(part, context.Result)) { return; } } } } private static bool CheckIsFinalPart(MimeBodyPart part, ICollection result) { Contract.Assert(part != null, "part cannot be null."); Contract.Assert(result != null, "result cannot be null."); if (part.IsComplete) { HttpContent partContent = part.GetCompletedHttpContent(); if (partContent != null) { result.Add(partContent); } bool isFinal = part.IsFinal; part.Dispose(); return isFinal; } return false; } /// /// Managing state for asynchronous read and write operations /// private class MultipartAsyncContext { public MultipartAsyncContext(Stream contentStream, MimeMultipartBodyPartParser mimeParser, byte[] data, ICollection result) { Contract.Assert(contentStream != null); Contract.Assert(mimeParser != null); Contract.Assert(data != null); ContentStream = contentStream; Result = result; MimeParser = mimeParser; Data = data; } /// /// Gets the that we read from. /// public Stream ContentStream { get; private set; } /// /// Gets the collection of parsed instances. /// public ICollection Result { get; private set; } /// /// The data buffer that we use for reading data from the input stream into before processing. /// public byte[] Data { get; private set; } /// /// Gets the MIME parser instance used to parse the data /// public MimeMultipartBodyPartParser MimeParser { get; private set; } } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpHeaderExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Net.Http.Headers; namespace System.Net.Http { internal static class HttpHeaderExtensions { public static void CopyTo(this HttpContentHeaders fromHeaders, HttpContentHeaders toHeaders) { Contract.Assert(fromHeaders != null, "fromHeaders cannot be null."); Contract.Assert(toHeaders != null, "toHeaders cannot be null."); foreach (KeyValuePair> header in fromHeaders) { toHeaders.TryAddWithoutValidation(header.Key, header.Value); } } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpMessageContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Derived class which can encapsulate an /// or an as an entity with media type "application/http". /// public class HttpMessageContent : HttpContent { private const string SP = " "; private const string ColonSP = ": "; private const string CRLF = "\r\n"; private const string CommaSeparator = ", "; private const int DefaultHeaderAllocation = 2 * 1024; private const string DefaultMediaType = "application/http"; private const string MsgTypeParameter = "msgtype"; private const string DefaultRequestMsgType = "request"; private const string DefaultResponseMsgType = "response"; private const string DefaultRequestMediaType = DefaultMediaType + "; " + MsgTypeParameter + "=" + DefaultRequestMsgType; private const string DefaultResponseMediaType = DefaultMediaType + "; " + MsgTypeParameter + "=" + DefaultResponseMsgType; // Set of header fields that only support single values such as Set-Cookie. private static readonly HashSet _singleValueHeaderFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "Cookie", "Set-Cookie", "X-Powered-By", }; // Set of header fields that should get serialized as space-separated values such as User-Agent. private static readonly HashSet _spaceSeparatedValueHeaderFields = new HashSet(StringComparer.OrdinalIgnoreCase) { "User-Agent", }; private bool _contentConsumed; private Lazy> _streamTask; /// /// Initializes a new instance of the class encapsulating an /// . /// /// The instance to encapsulate. public HttpMessageContent(HttpRequestMessage httpRequest) { if (httpRequest == null) { throw Error.ArgumentNull("httpRequest"); } HttpRequestMessage = httpRequest; Headers.ContentType = new MediaTypeHeaderValue(DefaultMediaType); Headers.ContentType.Parameters.Add(new NameValueHeaderValue(MsgTypeParameter, DefaultRequestMsgType)); InitializeStreamTask(); } /// /// Initializes a new instance of the class encapsulating an /// . /// /// The instance to encapsulate. public HttpMessageContent(HttpResponseMessage httpResponse) { if (httpResponse == null) { throw Error.ArgumentNull("httpResponse"); } HttpResponseMessage = httpResponse; Headers.ContentType = new MediaTypeHeaderValue(DefaultMediaType); Headers.ContentType.Parameters.Add(new NameValueHeaderValue(MsgTypeParameter, DefaultResponseMsgType)); InitializeStreamTask(); } private HttpContent Content { get { return HttpRequestMessage != null ? HttpRequestMessage.Content : HttpResponseMessage.Content; } } /// /// Gets the HTTP request message. /// public HttpRequestMessage HttpRequestMessage { get; private set; } /// /// Gets the HTTP response message. /// public HttpResponseMessage HttpResponseMessage { get; private set; } private void InitializeStreamTask() { _streamTask = new Lazy>(() => Content == null ? null : Content.ReadAsStreamAsync()); } /// /// Validates whether the content contains an HTTP Request or an HTTP Response. /// /// The content to validate. /// if set to true if the content is either an HTTP Request or an HTTP Response. /// Indicates whether validation failure should result in an or not. /// true if content is either an HTTP Request or an HTTP Response internal static bool ValidateHttpMessageContent(HttpContent content, bool isRequest, bool throwOnError) { if (content == null) { throw Error.ArgumentNull("content"); } MediaTypeHeaderValue contentType = content.Headers.ContentType; if (contentType != null) { if (!contentType.MediaType.Equals(DefaultMediaType, StringComparison.OrdinalIgnoreCase)) { if (throwOnError) { throw Error.Argument("content", Properties.Resources.HttpMessageInvalidMediaType, FormattingUtilities.HttpContentType.Name, isRequest ? DefaultRequestMediaType : DefaultResponseMediaType); } else { return false; } } foreach (NameValueHeaderValue parameter in contentType.Parameters) { if (parameter.Name.Equals(MsgTypeParameter, StringComparison.OrdinalIgnoreCase)) { string msgType = FormattingUtilities.UnquoteToken(parameter.Value); if (!msgType.Equals(isRequest ? DefaultRequestMsgType : DefaultResponseMsgType, StringComparison.OrdinalIgnoreCase)) { if (throwOnError) { throw Error.Argument("content", Properties.Resources.HttpMessageInvalidMediaType, FormattingUtilities.HttpContentType.Name, isRequest ? DefaultRequestMediaType : DefaultResponseMediaType); } else { return false; } } return true; } } } if (throwOnError) { throw Error.Argument("content", Properties.Resources.HttpMessageInvalidMediaType, FormattingUtilities.HttpContentType.Name, isRequest ? DefaultRequestMediaType : DefaultResponseMediaType); } else { return false; } } /// /// Asynchronously serializes the object's content to the given . /// /// The to which to write. /// The associated . /// A instance that is asynchronously serializing the object's content. protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) { if (stream == null) { throw Error.ArgumentNull("stream"); } byte[] header = SerializeHeader(); await stream.WriteAsync(header, 0, header.Length); if (Content != null) { Stream readStream = await _streamTask.Value; ValidateStreamForReading(readStream); await Content.CopyToAsync(stream); } } /// /// Computes the length of the stream if possible. /// /// The computed length of the stream. /// true if the length has been computed; otherwise false. [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1108:BlockStatementsMustNotContainEmbeddedComments", Justification = "The code is more readable with such comments")] protected override bool TryComputeLength(out long length) { // We have four states we could be in: // 0. We have content and it knows its ContentLength. // 1. We have content, but the task is still running or finished without success // 2. We have content, the task has finished successfully, and the stream came back as a null or non-seekable // 3. We have content, the task has finished successfully, and the stream is seekable, so we know its length // 4. We don't have content (streamTask.Value == null) // // For #1 and #2, we return false. // For #3, we return true & the size of our headers + the content length // For #4, we return true & the size of our headers length = 0; if (Content?.Headers.ContentLength is not null) { length = (long)Content.Headers.ContentLength; // Case #0 } else if (_streamTask.Value is not null) { Stream readStream; if (!_streamTask.Value.TryGetResult(out readStream) // Case #1 || readStream == null || !readStream.CanSeek) // Case #2 { length = -1; return false; } length = readStream.Length; // Case #3 } // We serialize header to a StringBuilder so that we can determine the length // following the pattern for HttpContent to try and determine the message length. // The perf overhead is no larger than for the other HttpContent implementations. byte[] header = SerializeHeader(); length += header.Length; return true; } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (disposing) { if (HttpRequestMessage != null) { HttpRequestMessage.Dispose(); HttpRequestMessage = null; } if (HttpResponseMessage != null) { HttpResponseMessage.Dispose(); HttpResponseMessage = null; } } base.Dispose(disposing); } /// /// Serializes the HTTP request line. /// /// Where to write the request line. /// The HTTP request. private static void SerializeRequestLine(StringBuilder message, HttpRequestMessage httpRequest) { Contract.Assert(message != null, "message cannot be null"); message.Append(httpRequest.Method + SP); message.Append(httpRequest.RequestUri.PathAndQuery + SP); message.Append(FormattingUtilities.HttpVersionToken + "/" + (httpRequest.Version != null ? httpRequest.Version.ToString(2) : "1.1") + CRLF); // Only insert host header if not already present. if (httpRequest.Headers.Host == null) { message.Append(FormattingUtilities.HttpHostHeader + ColonSP + httpRequest.RequestUri.Authority + CRLF); } } /// /// Serializes the HTTP status line. /// /// Where to write the status line. /// The HTTP response. private static void SerializeStatusLine(StringBuilder message, HttpResponseMessage httpResponse) { Contract.Assert(message != null, "message cannot be null"); message.Append(FormattingUtilities.HttpVersionToken + "/" + (httpResponse.Version != null ? httpResponse.Version.ToString(2) : "1.1") + SP); message.Append((int)httpResponse.StatusCode + SP); message.Append(httpResponse.ReasonPhrase + CRLF); } /// /// Serializes the header fields. /// /// Where to write the status line. /// The headers to write. private static void SerializeHeaderFields(StringBuilder message, HttpHeaders headers) { Contract.Assert(message != null, "message cannot be null"); if (headers != null) { foreach (KeyValuePair> header in headers) { if (_singleValueHeaderFields.Contains(header.Key)) { foreach (string value in header.Value) { message.Append(header.Key + ColonSP + value + CRLF); } } else if (_spaceSeparatedValueHeaderFields.Contains(header.Key)) { message.Append(header.Key + ColonSP + String.Join(SP, header.Value) + CRLF); } else { message.Append(header.Key + ColonSP + String.Join(CommaSeparator, header.Value) + CRLF); } } } } private byte[] SerializeHeader() { StringBuilder message = new StringBuilder(DefaultHeaderAllocation); HttpHeaders headers = null; HttpContent content = null; if (HttpRequestMessage != null) { SerializeRequestLine(message, HttpRequestMessage); headers = HttpRequestMessage.Headers; content = HttpRequestMessage.Content; } else { SerializeStatusLine(message, HttpResponseMessage); headers = HttpResponseMessage.Headers; content = HttpResponseMessage.Content; } SerializeHeaderFields(message, headers); if (content != null) { SerializeHeaderFields(message, content.Headers); } message.Append(CRLF); return Encoding.UTF8.GetBytes(message.ToString()); } private void ValidateStreamForReading(Stream stream) { // Stream is null case should be an extreme, incredibly unlikely corner case. Every HttpContent from // the framework (see dotnet/runtime or .NET Framework reference source) provides a non-null Stream // in the ReadAsStreamAsync task's return value. Likely need a poorly-designed derived HttpContent // to hit this. Mostly ignoring the fact this message doesn't make much sense for the case. if (stream is null || !stream.CanRead) { throw Error.NotSupported(Properties.Resources.NotSupported_UnreadableStream); } // If the content needs to be written to a target stream a 2nd time, then the stream must support // seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target // stream (e.g. a NetworkStream). if (_contentConsumed) { if (stream.CanSeek) { stream.Position = 0; } else { throw Error.InvalidOperation(Properties.Resources.HttpMessageContentAlreadyRead, FormattingUtilities.HttpContentType.Name, HttpRequestMessage != null ? FormattingUtilities.HttpRequestMessageType.Name : FormattingUtilities.HttpResponseMessageType.Name); } } _contentConsumed = true; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpRequestHeadersExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// Provides extension methods for the class. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpRequestHeadersExtensions { private const string Cookie = "Cookie"; /// /// Gets any cookie headers present in the request. Each Cookie header is /// represented as one instance. A /// contains information about the domain, path, and other cookie information as well as one or /// more instances. Each instance contains /// a cookie name and whatever cookie state is associate with that name. The state is in the form of a /// which on the wire is encoded as HTML Form URL-encoded data. /// This representation allows for multiple related "cookies" to be carried within the same /// Cookie header while still providing separation between each cookie state. A sample /// Cookie header is shown below. In this example, there are two /// with names stateA and stateB respectively. Further, each cookie state contains two name/value /// pairs (name1/value1 and name2/value2) and (name3/value3 and name4/value4). /// /// Cookie: stateA=name1=value1&name2=value2; stateB=name3=value3&name4=value4; domain=domain1; path=path1; /// /// /// The request headers /// A collection of instances. public static Collection GetCookies(this HttpRequestHeaders headers) { if (headers == null) { throw Error.ArgumentNull("headers"); } Collection result = new Collection(); IEnumerable cookieHeaders; if (headers.TryGetValues(Cookie, out cookieHeaders)) { foreach (string cookieHeader in cookieHeaders) { CookieHeaderValue cookieHeaderValue; if (CookieHeaderValue.TryParse(cookieHeader, out cookieHeaderValue)) { result.Add(cookieHeaderValue); } } } return result; } /// /// Gets any cookie headers present in the request which contains a with /// a name that matches the provided . For example, if there are two Cookie /// header fields looking like this: /// /// Cookie: stateA=name1=value1&name2=value2; stateB=name3=value3&name4=value4; domain=domain1; path=path1; /// Cookie: stateC=name5=value5&name6=value6; stateD=name7=value7&name8=value8; domain=domain2; path=path2; /// /// and name is stateD then only the second Cookie header will be returned. /// /// The request headers /// The name of the to match. /// A collection of instances with a matching . public static Collection GetCookies(this HttpRequestHeaders headers, string name) { if (name == null) { throw Error.ArgumentNull("name"); } IEnumerable cookieHeaderValues = GetCookies(headers); CookieHeaderValue[] matches = cookieHeaderValues.Where(header => header.Cookies.Any(state => String.Equals(state.Name, name, StringComparison.OrdinalIgnoreCase))).ToArray(); return new Collection(matches); } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpRequestMessageExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Web.Http; namespace System.Net.Http { /// /// Provides extension methods for the class. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpRequestMessageExtensions { /// /// Creates an wired up to the associated . /// /// The HTTP request. /// The HTTP status code. /// An initialized . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")] public static HttpResponseMessage CreateResponse(this HttpRequestMessage request, HttpStatusCode statusCode) { if (request == null) { throw Error.ArgumentNull("request"); } return new HttpResponseMessage { Content = new StreamContent(Stream.Null), StatusCode = statusCode, RequestMessage = request }; } /// /// Creates an wired up to the associated . /// /// The HTTP request. /// An initialized . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller will dispose")] public static HttpResponseMessage CreateResponse(this HttpRequestMessage request) { if (request == null) { throw Error.ArgumentNull("request"); } return new HttpResponseMessage { Content = new StreamContent(Stream.Null), RequestMessage = request }; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpResponseHeadersExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// Provides extension methods for the class. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class HttpResponseHeadersExtensions { private const string SetCookie = "Set-Cookie"; /// /// Adds cookies to a response. Each Set-Cookie header is /// represented as one instance. A /// contains information about the domain, path, and other cookie information as well as one or /// more instances. Each instance contains /// a cookie name and whatever cookie state is associate with that name. The state is in the form of a /// which on the wire is encoded as HTML Form URL-encoded data. /// This representation allows for multiple related "cookies" to be carried within the same /// Cookie header while still providing separation between each cookie state. A sample /// Cookie header is shown below. In this example, there are two /// with names state1 and state2 respectively. Further, each cookie state contains two name/value /// pairs (name1/value1 and name2/value2) and (name3/value3 and name4/value4). /// /// Set-Cookie: state1:name1=value1&name2=value2; state2:name3=value3&name4=value4; domain=domain1; path=path1; /// /// /// The response headers /// The cookie values to add to the response. public static void AddCookies(this HttpResponseHeaders headers, IEnumerable cookies) { if (headers == null) { throw Error.ArgumentNull("headers"); } if (cookies == null) { throw Error.ArgumentNull("cookies"); } foreach (CookieHeaderValue cookie in cookies) { if (cookie == null) { throw Error.Argument("cookies", Properties.Resources.CookieNull); } headers.TryAddWithoutValidation(SetCookie, cookie.ToString()); } } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpUnsortedHeaders.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; namespace System.Net.Http { /// /// All of the existing non-abstract implementations, namely /// , , and /// enforce strict rules on what kinds of HTTP header fields can be added to each collection. /// When parsing the "application/http" media type we need to just get the unsorted list. It /// will get sorted later. /// internal class HttpUnsortedHeaders : HttpHeaders { } } ================================================ FILE: src/System.Net.Http.Formatting/HttpUnsortedRequest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; namespace System.Net.Http { /// /// Represents the HTTP Request Line and header parameters parsed by /// and . /// internal class HttpUnsortedRequest { /// /// Initializes a new instance of the class. /// public HttpUnsortedRequest() { // Collection of unsorted headers. Later we will sort it into the appropriate // HttpContentHeaders, HttpRequestHeaders, and HttpResponseHeaders. HttpHeaders = new HttpUnsortedHeaders(); } /// /// Gets or sets the HTTP method. /// /// /// The HTTP method. /// public HttpMethod Method { get; set; } /// /// Gets or sets the HTTP request URI portion that is carried in the RequestLine (i.e the URI path + query). /// /// /// The request URI. /// public string RequestUri { get; set; } /// /// Gets or sets the HTTP version. /// /// /// The HTTP version. /// public Version Version { get; set; } /// /// Gets the unsorted HTTP request headers. /// public HttpHeaders HttpHeaders { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/HttpUnsortedResponse.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; namespace System.Net.Http { /// /// Represents the HTTP Status Line and header parameters parsed by /// and . /// internal class HttpUnsortedResponse { /// /// Initializes a new instance of the class. /// public HttpUnsortedResponse() { // Collection of unsorted headers. Later we will sort it into the appropriate // HttpContentHeaders, HttpRequestHeaders, and HttpResponseHeaders. HttpHeaders = new HttpUnsortedHeaders(); } /// /// Gets or sets the HTTP version. /// /// /// The HTTP version. /// public Version Version { get; set; } /// /// Gets or sets the /// /// /// The HTTP status code /// public HttpStatusCode StatusCode { get; set; } /// /// Gets or sets the HTTP reason phrase /// /// /// The response reason phrase /// public string ReasonPhrase { get; set; } /// /// Gets the unsorted HTTP request headers. /// public HttpHeaders HttpHeaders { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/AsyncResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Threading; using System.Web.Http; namespace System.Net.Http.Internal { [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "_manualResetEvent is disposed in End")] internal abstract class AsyncResult : IAsyncResult { private AsyncCallback _callback; private object _state; private bool _isCompleted; private bool _completedSynchronously; private bool _endCalled; private Exception _exception; protected AsyncResult(AsyncCallback callback, object state) { _callback = callback; _state = state; } public object AsyncState { get { return _state; } } public WaitHandle AsyncWaitHandle { get { Contract.Assert(false, "AsyncWaitHandle is not supported -- use callbacks instead."); return null; } } public bool CompletedSynchronously { get { return _completedSynchronously; } } public bool HasCallback { get { return _callback != null; } } public bool IsCompleted { get { return _isCompleted; } } protected void Complete(bool completedSynchronously) { if (_isCompleted) { throw Error.InvalidOperation(Properties.Resources.AsyncResult_MultipleCompletes, GetType().Name); } _completedSynchronously = completedSynchronously; _isCompleted = true; if (_callback != null) { try { _callback(this); } catch (Exception e) { throw Error.InvalidOperation(e, Properties.Resources.AsyncResult_CallbackThrewException); } } } protected void Complete(bool completedSynchronously, Exception exception) { _exception = exception; Complete(completedSynchronously); } protected static TAsyncResult End(IAsyncResult result) where TAsyncResult : AsyncResult { if (result == null) { throw Error.ArgumentNull("result"); } TAsyncResult thisPtr = result as TAsyncResult; if (thisPtr == null) { throw Error.Argument("result", Properties.Resources.AsyncResult_ResultMismatch); } if (!thisPtr._isCompleted) { thisPtr.AsyncWaitHandle.WaitOne(); } if (thisPtr._endCalled) { throw Error.InvalidOperation(Properties.Resources.AsyncResult_MultipleEnds); } thisPtr._endCalled = true; if (thisPtr._exception != null) { throw thisPtr._exception; } return thisPtr; } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/ByteRangeStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http.Internal { /// /// Stream which only exposes a read-only only range view of an /// inner stream. /// internal class ByteRangeStream : DelegatingStream { // The offset stream position at which the range starts. private readonly long _lowerbounds; // The total number of bytes within the range. private readonly long _totalCount; // The current number of bytes read into the range private long _currentCount; public ByteRangeStream(Stream innerStream, RangeItemHeaderValue range) : base(innerStream) { if (range == null) { throw Error.ArgumentNull("range"); } if (!innerStream.CanSeek) { throw Error.Argument("innerStream", Properties.Resources.ByteRangeStreamNotSeekable, typeof(ByteRangeStream).Name); } if (innerStream.Length < 1) { throw Error.ArgumentOutOfRange("innerStream", innerStream.Length, Properties.Resources.ByteRangeStreamEmpty, typeof(ByteRangeStream).Name); } if (range.From.HasValue && range.From.Value > innerStream.Length) { throw Error.ArgumentOutOfRange("range", range.From, Properties.Resources.ByteRangeStreamInvalidFrom, innerStream.Length); } // Ranges are inclusive so 0-9 means the first 10 bytes long maxLength = innerStream.Length - 1; long upperbounds; if (range.To.HasValue) { if (range.From.HasValue) { // e.g bytes=0-499 (the first 500 bytes offsets 0-499) upperbounds = Math.Min(range.To.Value, maxLength); _lowerbounds = range.From.Value; } else { // e.g bytes=-500 (the final 500 bytes) upperbounds = maxLength; _lowerbounds = Math.Max(innerStream.Length - range.To.Value, 0); } } else { if (range.From.HasValue) { // e.g bytes=500- (from byte offset 500 and up) upperbounds = maxLength; _lowerbounds = range.From.Value; } else { // e.g. bytes=- (invalid so will never get here) upperbounds = maxLength; _lowerbounds = 0; } } _totalCount = upperbounds - _lowerbounds + 1; ContentRange = new ContentRangeHeaderValue(_lowerbounds, upperbounds, innerStream.Length); } public ContentRangeHeaderValue ContentRange { get; private set; } public override long Length { get { return _totalCount; } } public override bool CanWrite { get { return false; } } public override long Position { get { return _currentCount; } set { if (value < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, 0L); } _currentCount = value; } } #if !NETSTANDARD1_3 // BeginX and EndX are not supported on Streams in netstandard1.3 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return base.BeginRead(buffer, offset, PrepareStreamForRangeRead(count), callback, state); } #endif public override int Read(byte[] buffer, int offset, int count) { return base.Read(buffer, offset, PrepareStreamForRangeRead(count)); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return base.ReadAsync(buffer, offset, PrepareStreamForRangeRead(count), cancellationToken); } public override int ReadByte() { int effectiveCount = PrepareStreamForRangeRead(1); if (effectiveCount <= 0) { return -1; } return base.ReadByte(); } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: _currentCount = offset; break; case SeekOrigin.Current: _currentCount = _currentCount + offset; break; case SeekOrigin.End: _currentCount = _totalCount + offset; break; default: throw Error.InvalidEnumArgument("origin", (int)origin, typeof(SeekOrigin)); } if (_currentCount < 0L) { throw new IOException(Properties.Resources.ByteRangeStreamInvalidOffset); } return _currentCount; } public override void SetLength(long value) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } public override void Write(byte[] buffer, int offset, int count) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } #if !NETSTANDARD1_3 // BeginX and EndX are not supported on Streams in netstandard1.3 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } public override void EndWrite(IAsyncResult asyncResult) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } #endif public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } public override void WriteByte(byte value) { throw Error.NotSupported(Properties.Resources.ByteRangeStreamReadOnly); } /// /// Gets the correct count for the next read operation. /// /// The count requested to be read by the caller. /// The remaining bytes to read within the range defined for this stream. private int PrepareStreamForRangeRead(int count) { // A negative count causes base.Raad* methods to throw an ArgumentOutOfRangeException. if (count <= 0) { return count; } // Reading past the end simply does nothing. if (_currentCount >= _totalCount) { return 0; } long effectiveCount = Math.Min(count, _totalCount - _currentCount); // Check if we should update the inner stream's position. var newPosition = _lowerbounds + _currentCount; var position = InnerStream.Position; if (newPosition != position) { InnerStream.Position = newPosition; } // Update current number of bytes read. _currentCount += effectiveCount; // Effective count can never be bigger than int. return (int)effectiveCount; } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/DelegatingStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http.Internal { /// /// Stream that delegates to inner stream. /// This is taken from System.Net.Http /// internal abstract class DelegatingStream : Stream { private readonly Stream _innerStream; protected DelegatingStream(Stream innerStream) { if (innerStream == null) { throw Error.ArgumentNull("innerStream"); } _innerStream = innerStream; } protected Stream InnerStream { get { return _innerStream; } } public override bool CanRead { get { return _innerStream.CanRead; } } public override bool CanSeek { get { return _innerStream.CanSeek; } } public override bool CanWrite { get { return _innerStream.CanWrite; } } public override long Length { get { return _innerStream.Length; } } public override long Position { get { return _innerStream.Position; } set { _innerStream.Position = value; } } public override int ReadTimeout { get { return _innerStream.ReadTimeout; } set { _innerStream.ReadTimeout = value; } } public override bool CanTimeout { get { return _innerStream.CanTimeout; } } public override int WriteTimeout { get { return _innerStream.WriteTimeout; } set { _innerStream.WriteTimeout = value; } } protected override void Dispose(bool disposing) { if (disposing) { _innerStream.Dispose(); } base.Dispose(disposing); } public override long Seek(long offset, SeekOrigin origin) { return _innerStream.Seek(offset, origin); } public override int Read(byte[] buffer, int offset, int count) { return _innerStream.Read(buffer, offset, count); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return _innerStream.ReadAsync(buffer, offset, count, cancellationToken); } #if !NETSTANDARD1_3 // BeginX and EndX not supported on Streams in netstandard1.3 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return _innerStream.BeginRead(buffer, offset, count, callback, state); } public override int EndRead(IAsyncResult asyncResult) { return _innerStream.EndRead(asyncResult); } #endif public override int ReadByte() { return _innerStream.ReadByte(); } public override void Flush() { _innerStream.Flush(); } public override Task FlushAsync(CancellationToken cancellationToken) { return _innerStream.FlushAsync(cancellationToken); } public override void SetLength(long value) { _innerStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { _innerStream.Write(buffer, offset, count); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return _innerStream.WriteAsync(buffer, offset, count, cancellationToken); } #if !NETSTANDARD1_3 // BeginX and EndX not supported on Streams in netstandard1.3 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return _innerStream.BeginWrite(buffer, offset, count, callback, state); } public override void EndWrite(IAsyncResult asyncResult) { _innerStream.EndWrite(asyncResult); } #endif public override void WriteByte(byte value) { _innerStream.WriteByte(value); } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/HttpValueCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.Contracts; using System.Linq; using System.Net.Http.Internal; using System.Runtime.Serialization; using System.Text; using System.Web.Http; namespace System.Net.Http.Formatting.Internal { /// /// NameValueCollection to represent form data and to generate form data output. /// #if !NETSTANDARD1_3 // NameValueCollection is not serializable in netstandard1.3. [Serializable] #endif internal class HttpValueCollection : NameValueCollection { #if !NETSTANDARD1_3 // NameValueCollection is not serializable in netstandard1.3. protected HttpValueCollection(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif private HttpValueCollection() : base(StringComparer.OrdinalIgnoreCase) // case-insensitive keys { } // Use a builder function instead of a ctor to avoid virtual calls from the ctor. // The above condition is only important in the Full .NET fx implementation. internal static HttpValueCollection Create() { return new HttpValueCollection(); } internal static HttpValueCollection Create(IEnumerable> pairs) { Contract.Assert(pairs != null); var hvc = new HttpValueCollection(); // Ordering example: // k=A&j=B&k=C --> k:[A,C];j=[B]. foreach (KeyValuePair kvp in pairs) { hvc.Add(kvp.Key, kvp.Value); } hvc.IsReadOnly = false; return hvc; } /// /// Adds a name-value pair to the collection. /// /// The name to be added as a case insensitive string. /// The value to be added. public override void Add(string name, string value) { ThrowIfMaxHttpCollectionKeysExceeded(Count); name = name ?? String.Empty; value = value ?? String.Empty; base.Add(name, value); } /// /// Converts the content of this instance to its equivalent string representation. /// /// The string representation of the value of this instance, multiple values with a single key are comma separated. public override string ToString() { return ToString(true); } private static void ThrowIfMaxHttpCollectionKeysExceeded(int count) { if (count >= MediaTypeFormatter.MaxHttpCollectionKeys) { throw Error.InvalidOperation(System.Net.Http.Properties.Resources.MaxHttpCollectionKeyLimitReached, MediaTypeFormatter.MaxHttpCollectionKeys, typeof(MediaTypeFormatter)); } } private string ToString(bool urlEncode) { if (Count == 0) { return String.Empty; } StringBuilder builder = new StringBuilder(); bool first = true; foreach (string name in this) { string[] values = GetValues(name); if (values == null || values.Length == 0) { first = AppendNameValuePair(builder, first, urlEncode, name, String.Empty); } else { foreach (string value in values) { first = AppendNameValuePair(builder, first, urlEncode, name, value); } } } return builder.ToString(); } private static bool AppendNameValuePair(StringBuilder builder, bool first, bool urlEncode, string name, string value) { string effectiveName = name ?? String.Empty; string encodedName = urlEncode ? WebUtility.UrlEncode(effectiveName) : effectiveName; string effectiveValue = value ?? String.Empty; string encodedValue = urlEncode ? WebUtility.UrlEncode(effectiveValue) : effectiveValue; if (first) { first = false; } else { builder.Append("&"); } builder.Append(encodedName); if (!String.IsNullOrEmpty(encodedValue)) { builder.Append("="); builder.Append(encodedValue); } return first; } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/NonClosingDelegatingStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; namespace System.Net.Http.Internal { /// /// Stream that doesn't close the inner stream when closed. This is to work around a limitation /// in the insisting of closing the inner stream. /// The regular does allow for not closing the inner stream but that /// doesn't have the quota that we need for security reasons. Implementations of /// /// should not close the input stream when reading or writing so hence this workaround. /// internal class NonClosingDelegatingStream : DelegatingStream { public NonClosingDelegatingStream(Stream innerStream) : base(innerStream) { } #if NETSTANDARD1_3 protected override void Dispose(bool disposing) { } #else public override void Close() { } #endif } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/NullableAttributes.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // From https://github.com/dotnet/runtime/blob/88868b7a781f4e5b9037b8721f30440207a7aa42/src/tools/illink/src/ILLink.RoslynAnalyzer/NullableAttributes.cs namespace System.Diagnostics.CodeAnalysis { #if !NETSTANDARD2_1 /// Specifies that null is allowed as an input even if the corresponding type disallows it. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class AllowNullAttribute : Attribute { } /// Specifies that null is disallowed as an input even if the corresponding type allows it. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class DisallowNullAttribute : Attribute { } /// Specifies that an output may be null even if the corresponding type disallows it. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class MaybeNullAttribute : Attribute { } /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class NotNullAttribute : Attribute { } /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class MaybeNullWhenAttribute : Attribute { /// Initializes the attribute with the specified return value condition. /// /// The return value condition. If the method returns this value, the associated parameter may be null. /// public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; /// Gets the return value condition. public bool ReturnValue { get; } } /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class NotNullWhenAttribute : Attribute { /// Initializes the attribute with the specified return value condition. /// /// The return value condition. If the method returns this value, the associated parameter will not be null. /// public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; /// Gets the return value condition. public bool ReturnValue { get; } } /// Specifies that the output will be non-null if the named parameter is non-null. [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class NotNullIfNotNullAttribute : Attribute { /// Initializes the attribute with the associated parameter name. /// /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. /// public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; /// Gets the associated parameter name. public string ParameterName { get; } } /// Applied to a method that will never return under any circumstance. [AttributeUsage(AttributeTargets.Method, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class DoesNotReturnAttribute : Attribute { } /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class DoesNotReturnIfAttribute : Attribute { /// Initializes the attribute with the specified parameter value. /// /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to /// the associated parameter matches this value. /// public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; /// Gets the condition parameter value. public bool ParameterValue { get; } } #endif /// Specifies that the method or property will ensure that the listed field and property members have not-null values. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class MemberNotNullAttribute : Attribute { /// Initializes the attribute with a field or property member. /// /// The field or property member that is promised to be not-null. /// public MemberNotNullAttribute(string member) => Members = new[] { member }; /// Initializes the attribute with the list of field and property members. /// /// The list of field and property members that are promised to be not-null. /// public MemberNotNullAttribute(params string[] members) => Members = members; /// Gets field or property member names. public string[] Members { get; } } /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] #if SYSTEM_PRIVATE_CORELIB public #else internal #endif sealed class MemberNotNullWhenAttribute : Attribute { /// Initializes the attribute with the specified return value condition and a field or property member. /// /// The return value condition. If the method returns this value, the associated parameter will not be null. /// /// /// The field or property member that is promised to be not-null. /// public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new[] { member }; } /// Initializes the attribute with the specified return value condition and list of field and property members. /// /// The return value condition. If the method returns this value, the associated parameter will not be null. /// /// /// The list of field and property members that are promised to be not-null. /// public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } /// Gets the return value condition. public bool ReturnValue { get; } /// Gets field or property member names. public string[] Members { get; } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/TranscodingStream.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // From https://github.com/dotnet/runtime/blob/88868b7a781f4e5b9037b8721f30440207a7aa42/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs using System.Buffers; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using Properties = System.Net.Http.Properties; #nullable enable namespace System.Text { internal sealed class TranscodingStream : Stream { private const int DefaultReadByteBufferSize = 4 * 1024; // lifted from StreamReader.cs (FileStream) // We optimistically assume 1 byte ~ 1 char during transcoding. This is a good rule of thumb // but isn't always appropriate: transcoding between single-byte and multi-byte encodings // will violate this, as will any invalid data fixups performed by the transcoder itself. // To account for these unknowns we have a minimum scratch buffer size we use during the // transcoding process. This should be generous enough to account for even the largest // fallback mechanism we're likely to see in the real world. private const int MinWriteRentedArraySize = 4 * 1024; private const int MaxWriteRentedArraySize = 1024 * 1024; private static readonly byte[] EmptyByteBuffer = new byte[0]; private static readonly char[] EmptyCharBuffer = new char[0]; private readonly Encoding _innerEncoding; private readonly Encoding _thisEncoding; private Stream _innerStream; // null if the wrapper has been disposed private readonly bool _leaveOpen; private readonly byte[] _singleByteBuffer = new byte[1]; /* * Fields used for writing bytes [this] -> chars -> bytes [inner] * Lazily initialized the first time we need to write */ private Encoder? _innerEncoder; private Decoder? _thisDecoder; /* * Fields used for reading bytes [inner] -> chars -> bytes [this] * Lazily initialized the first time we need to read */ private Encoder? _thisEncoder; private Decoder? _innerDecoder; private int _readCharBufferMaxSize; // the maximum number of characters _innerDecoder.ReadChars can return private byte[]? _readBuffer; // contains the data that Read() should return private int _readBufferOffset; private int _readBufferCount; internal TranscodingStream(Stream innerStream, Encoding innerEncoding, Encoding thisEncoding, bool leaveOpen = false) { _innerStream = innerStream ?? throw Error.ArgumentNull(nameof(innerStream)); _leaveOpen = leaveOpen; _innerEncoding = innerEncoding ?? throw Error.ArgumentNull(nameof(innerEncoding)); _thisEncoding = thisEncoding ?? throw Error.ArgumentNull(nameof(thisEncoding)); } /* * Most CanXyz methods delegate to the inner stream, returning false * if this instance has been disposed. CanSeek is always false. */ public override bool CanRead => _innerStream?.CanRead ?? false; public override bool CanSeek => false; public override bool CanWrite => _innerStream?.CanWrite ?? false; public override long Length => throw Error.NotSupported(Properties.Resources.NotSupported_UnseekableStream); public override long Position { get => throw Error.NotSupported(Properties.Resources.NotSupported_UnseekableStream); set => throw Error.NotSupported(Properties.Resources.NotSupported_UnseekableStream); } protected override void Dispose(bool disposing) { Debug.Assert(disposing, "This type isn't finalizable."); base.Dispose(disposing); if (_innerStream is null) { return; // dispose called multiple times, ignore } // First, flush any pending data to the inner stream. ArraySegment pendingData = FinalFlushWriteBuffers(); if (pendingData.Count != 0) { _innerStream.Write(pendingData.Array, pendingData.Offset, pendingData.Count); } // Mark our object as disposed Stream innerStream = _innerStream; _innerStream = null!; // And dispose the inner stream if needed if (!_leaveOpen) { innerStream.Dispose(); } } #if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1 public override ValueTask DisposeAsync() { if (_innerStream is null) { return default; // dispose called multiple times, ignore } // First, get any pending data destined for the inner stream. ArraySegment pendingData = FinalFlushWriteBuffers(); if (pendingData.Count == 0) { // Fast path: just dispose of the object graph. // No need to write anything to the stream first. Stream innerStream = _innerStream; _innerStream = null!; return (_leaveOpen) ? default /* no work to do */ : innerStream.DisposeAsync(); } // Slower path; need to perform an async write followed by an async dispose. return DisposeAsyncCore(pendingData); async ValueTask DisposeAsyncCore(ArraySegment pendingData) { Debug.Assert(pendingData.Count != 0); Stream innerStream = _innerStream; _innerStream = null!; await innerStream.WriteAsync(pendingData.AsMemory()).ConfigureAwait(false); if (!_leaveOpen) { await innerStream.DisposeAsync().ConfigureAwait(false); } } } #endif #pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant #pragma warning disable CS8774 // Member must have a non-null value when exiting. // Sets up the data structures that are necessary before any read operation takes place, // throwing if the object is in a state where reads are not possible. [MemberNotNull(nameof(_innerDecoder), nameof(_thisEncoder), nameof(_readBuffer))] private void EnsurePreReadConditions() { ThrowIfDisposed(); if (_innerDecoder is null) { InitializeReadDataStructures(); } void InitializeReadDataStructures() { if (!CanRead) { throw Error.NotSupported(Properties.Resources.NotSupported_UnreadableStream); } _innerDecoder = _innerEncoding.GetDecoder(); _thisEncoder = _thisEncoding.GetEncoder(); _readCharBufferMaxSize = _innerEncoding.GetMaxCharCount(DefaultReadByteBufferSize); // Can't use ArrayPool for the below array since it's an instance field of this object. // But since we never expose the raw array contents to our callers we can get away // with skipping the array zero-init during allocation. The segment points to the // data which we haven't yet read; however, we own the entire backing array and can // re-create the segment as needed once the array is repopulated. #if NET5_0_OR_GREATER _readBuffer = GC.AllocateUninitializedArray(_thisEncoding.GetMaxByteCount(_readCharBufferMaxSize)); #else _readBuffer = new byte[_thisEncoding.GetMaxByteCount(_readCharBufferMaxSize)]; #endif } } // Sets up the data structures that are necessary before any write operation takes place, // throwing if the object is in a state where writes are not possible. [MemberNotNull(nameof(_thisDecoder), nameof(_innerEncoder))] private void EnsurePreWriteConditions() { ThrowIfDisposed(); if (_innerEncoder is null) { InitializeReadDataStructures(); } void InitializeReadDataStructures() { if (!CanWrite) { throw Error.NotSupported(Properties.Resources.NotSupported_UnwritableStream); } _innerEncoder = _innerEncoding.GetEncoder(); _thisDecoder = _thisEncoding.GetDecoder(); } } #pragma warning restore CS8774 // Member must have a non-null value when exiting. #pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant // returns any pending data that needs to be flushed to the inner stream before disposal private ArraySegment FinalFlushWriteBuffers() { // If this stream was never used for writing, no-op. if (_thisDecoder is null || _innerEncoder is null) { return default; } // convert bytes [this] -> chars // Having leftover data in our buffers should be very rare since it should only // occur if the end of the stream contains an incomplete multi-byte sequence. // Let's not bother complicating this logic with array pool rentals or allocation- // avoiding loops. char[] chars = EmptyCharBuffer; int charCount = _thisDecoder.GetCharCount(EmptyByteBuffer, 0, 0, flush: true); if (charCount > 0) { chars = new char[charCount]; charCount = _thisDecoder.GetChars(EmptyByteBuffer, 0, 0, chars, 0, flush: true); } // convert chars -> bytes [inner] // It's possible that _innerEncoder might need to perform some end-of-text fixup // (due to flush: true), even if _thisDecoder didn't need to do so. byte[] bytes = EmptyByteBuffer; int byteCount = _innerEncoder.GetByteCount(chars, 0, charCount, flush: true); if (byteCount > 0) { bytes = new byte[byteCount]; byteCount = _innerEncoder.GetBytes(chars, 0, charCount, bytes, 0, flush: true); } return new ArraySegment(bytes, 0, byteCount); } public override void Flush() { // Don't pass flush: true to our inner decoder + encoder here, since it could cause data // corruption if a flush occurs mid-stream. Wait until the stream is being closed. ThrowIfDisposed(); _innerStream.Flush(); } public override Task FlushAsync(CancellationToken cancellationToken) { // Don't pass flush: true to our inner decoder + encoder here, since it could cause data // corruption if a flush occurs mid-stream. Wait until the stream is being closed. ThrowIfDisposed(); return _innerStream.FlushAsync(cancellationToken); } public override int Read(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); return Read(new Span(buffer, offset, count)); } #if NETCOREAPP || NETSTANDARD2_1 public override #else private #endif int Read(Span buffer) { EnsurePreReadConditions(); // If there's no data in our pending read buffer, we'll need to populate it from // the inner stream. We read the inner stream's bytes, decode that to chars using // the 'inner' encoding, then re-encode those chars under the 'this' encoding. // We've already calculated the worst-case expansions for the intermediate buffers, // so we use GetChars / GetBytes instead of Convert to simplify the below code // and to ensure an exception is thrown if the Encoding reported an incorrect // worst-case expansion. if (_readBufferCount == 0) { byte[] rentedBytes = ArrayPool.Shared.Rent(DefaultReadByteBufferSize); char[] rentedChars = ArrayPool.Shared.Rent(_readCharBufferMaxSize); try { int pendingReadDataPopulatedJustNow; bool isEofReached; do { // Beware: Use our constant value instead of 'rentedBytes.Length' for the count // parameter below. The reason for this is that the array pool could've returned // a larger-than-expected array, but our worst-case expansion calculations // performed earlier didn't take that into account. int innerBytesReadJustNow = _innerStream.Read(rentedBytes, 0, DefaultReadByteBufferSize); isEofReached = (innerBytesReadJustNow == 0); // Convert bytes [inner] -> chars, then convert chars -> bytes [this]. // We can't return 0 to our caller until inner stream EOF has been reached. But if the // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway // since it can't yet make forward progress on the input data. If this happens, we'll // loop so that we don't return 0 to our caller until we truly see inner stream EOF. int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); } while (!isEofReached && pendingReadDataPopulatedJustNow == 0); _readBufferOffset = 0; _readBufferCount = pendingReadDataPopulatedJustNow; } finally { ArrayPool.Shared.Return(rentedBytes); ArrayPool.Shared.Return(rentedChars); } } // At this point: (a) we've populated our pending read buffer and there's // useful data to return to our caller; or (b) the pending read buffer is // empty because the inner stream has reached EOF and all pending read data // has already been flushed, and we should return 0. int bytesToReturn = Math.Min(_readBufferCount, buffer.Length); _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer); _readBufferOffset += bytesToReturn; _readBufferCount -= bytesToReturn; return bytesToReturn; } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); return ReadAsync(new Memory(buffer, offset, count), cancellationToken).AsTask(); } #if NETCOREAPP || NETSTANDARD2_1 public override #else private #endif ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { EnsurePreReadConditions(); if (cancellationToken.IsCancellationRequested) { #if NETCOREAPP || NETSTANDARD return new ValueTask(Task.FromCanceled(cancellationToken)); #else // Lose track of the CancellationToken in this case. return new ValueTask(TaskHelpers.Canceled()); #endif } return ReadAsyncCore(buffer, cancellationToken); async ValueTask ReadAsyncCore(Memory buffer, CancellationToken cancellationToken) { // If there's no data in our pending read buffer, we'll need to populate it from // the inner stream. We read the inner stream's bytes, decode that to chars using // the 'inner' encoding, then re-encode those chars under the 'this' encoding. // We've already calculated the worst-case expansions for the intermediate buffers, // so we use GetChars / GetBytes instead of Convert to simplify the below code // and to ensure an exception is thrown if the Encoding reported an incorrect // worst-case expansion. if (_readBufferCount == 0) { byte[] rentedBytes = ArrayPool.Shared.Rent(DefaultReadByteBufferSize); char[] rentedChars = ArrayPool.Shared.Rent(_readCharBufferMaxSize); try { int pendingReadDataPopulatedJustNow; bool isEofReached; do { // Beware: Use our constant value instead of 'rentedBytes.Length' when creating // the Mem struct. The reason for this is that the array pool could've returned // a larger-than-expected array, but our worst-case expansion calculations // performed earlier didn't take that into account. int innerBytesReadJustNow = await _innerStream.ReadAsync(rentedBytes, 0, DefaultReadByteBufferSize, cancellationToken).ConfigureAwait(false); isEofReached = (innerBytesReadJustNow == 0); // Convert bytes [inner] -> chars, then convert chars -> bytes [this]. // We can't return 0 to our caller until inner stream EOF has been reached. But if the // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway // since it can't yet make forward progress on the input data. If this happens, we'll // loop so that we don't return 0 to our caller until we truly see inner stream EOF. int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedChars, 0, flush: isEofReached); pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _readBuffer, 0, flush: isEofReached); } while (!isEofReached && pendingReadDataPopulatedJustNow == 0); _readBufferOffset = 0; _readBufferCount = pendingReadDataPopulatedJustNow; } finally { ArrayPool.Shared.Return(rentedBytes); ArrayPool.Shared.Return(rentedChars); } } // At this point: (a) we've populated our pending read buffer and there's // useful data to return to our caller; or (b) the pending read buffer is // empty because the inner stream has reached EOF and all pending read data // has already been flushed, and we should return 0. int bytesToReturn = Math.Min(_readBufferCount, buffer.Length); _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer.Span); _readBufferOffset += bytesToReturn; _readBufferCount -= bytesToReturn; return bytesToReturn; } } public override int ReadByte() { return Read(_singleByteBuffer, offset: 0, count: 1) != 0 ? _singleByteBuffer[0] : -1; } public override long Seek(long offset, SeekOrigin origin) => throw Error.NotSupported(Properties.Resources.NotSupported_UnseekableStream); public override void SetLength(long value) => throw Error.NotSupported(Properties.Resources.NotSupported_UnseekableStream); #if NET6_0_OR_GREATER [StackTraceHidden] #endif private void ThrowIfDisposed() { if (_innerStream is null) { ThrowObjectDisposedException(); } } [DoesNotReturn] #if NET6_0_OR_GREATER [StackTraceHidden] #endif private void ThrowObjectDisposedException() { throw new ObjectDisposedException(GetType().Name, Properties.Resources.ObjectDisposed_StreamClosed); } public override void Write(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); #if NETCOREAPP || NETSTANDARD2_1 Write(new ReadOnlySpan(buffer, offset, count)); #else WriteCore(buffer, offset, count); #endif } #if NETCOREAPP || NETSTANDARD2_1 public override void Write(ReadOnlySpan buffer) { EnsurePreWriteConditions(); if (buffer.IsEmpty) { return; } int rentalLength = buffer.Length < MinWriteRentedArraySize ? MinWriteRentedArraySize : buffer.Length > MaxWriteRentedArraySize ? MaxWriteRentedArraySize : buffer.Length; char[] scratchChars = ArrayPool.Shared.Rent(rentalLength); byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength); try { bool decoderFinished, encoderFinished; do { // convert bytes [this] -> chars _thisDecoder.Convert( bytes: buffer, chars: scratchChars, flush: false, out int bytesConsumed, out int charsWritten, out decoderFinished); buffer = buffer.Slice(bytesConsumed); // convert chars -> bytes [inner] Span decodedChars = scratchChars.AsSpan(0, charsWritten); do { _innerEncoder.Convert( chars: decodedChars, bytes: scratchBytes, flush: false, out int charsConsumed, out int bytesWritten, out encoderFinished); decodedChars = decodedChars.Slice(charsConsumed); // It's more likely that the inner stream provides an optimized implementation of // Write(byte[], ...) over Write(ROS), so we'll prefer the byte[]-based overloads. _innerStream.Write(scratchBytes, 0, bytesWritten); } while (!encoderFinished); } while (!decoderFinished); } finally { ArrayPool.Shared.Return(scratchChars); ArrayPool.Shared.Return(scratchBytes); } } #else private void WriteCore(byte[] buffer, int offset, int count) { EnsurePreWriteConditions(); if (count == 0) { return; } int rentalLength = buffer.Length < MinWriteRentedArraySize ? MinWriteRentedArraySize : buffer.Length > MaxWriteRentedArraySize ? MaxWriteRentedArraySize : buffer.Length; char[] scratchChars = ArrayPool.Shared.Rent(rentalLength); byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength); try { bool decoderFinished, encoderFinished; do { // convert bytes [this] -> chars _thisDecoder.Convert( bytes: buffer, byteIndex: offset, byteCount: count, chars: scratchChars, charIndex: 0, charCount: rentalLength, flush: false, out int bytesConsumed, out int charsWritten, out decoderFinished); offset += bytesConsumed; count -= bytesConsumed; // convert chars -> bytes [inner] int scratchOffset = 0; do { _innerEncoder.Convert( chars: scratchChars, charIndex: scratchOffset, charCount: charsWritten, bytes: scratchBytes, byteIndex: 0, byteCount: rentalLength, flush: false, out int charsConsumed, out int bytesWritten, out encoderFinished); scratchOffset += charsConsumed; charsWritten -= charsConsumed; // It's more likely that the inner stream provides an optimized implementation of // Write(byte[], ...) over Write(ROS), so we'll prefer the byte[]-based overloads. _innerStream.Write(scratchBytes, 0, bytesWritten); } while (!encoderFinished); } while (!decoderFinished); } finally { ArrayPool.Shared.Return(scratchChars); ArrayPool.Shared.Return(scratchBytes); } } #endif public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { ValidateBufferArguments(buffer, offset, count); #if NETCOREAPP || NETSTANDARD2_1 return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); #else return WriteAsyncCore(buffer, offset, count, cancellationToken).AsTask(); #endif } #if NETCOREAPP || NETSTANDARD2_1 public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { EnsurePreWriteConditions(); if (cancellationToken.IsCancellationRequested) { return new ValueTask(Task.FromCanceled(cancellationToken)); } if (buffer.IsEmpty) { // ValueTask.CompletedTask return default; } return WriteAsyncCore(buffer, cancellationToken); async ValueTask WriteAsyncCore(ReadOnlyMemory remainingOuterEncodedBytes, CancellationToken cancellationToken) { int rentalLength = remainingOuterEncodedBytes.Length < MinWriteRentedArraySize ? MinWriteRentedArraySize : remainingOuterEncodedBytes.Length > MaxWriteRentedArraySize ? MaxWriteRentedArraySize: remainingOuterEncodedBytes.Length; char[] scratchChars = ArrayPool.Shared.Rent(rentalLength); byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength); try { bool decoderFinished, encoderFinished; do { // convert bytes [this] -> chars _thisDecoder.Convert( bytes: buffer, chars: scratchChars, flush: false, out int bytesConsumed, out int charsWritten, out decoderFinished); buffer = buffer.Slice(bytesConsumed); // convert chars -> bytes [inner] Span decodedChars = scratchChars.AsSpan(0, charsWritten); do { _innerEncoder.Convert( chars: decodedChars, bytes: scratchBytes, flush: false, out int charsConsumed, out int bytesWritten, out encoderFinished); decodedChars = decodedChars.Slice(charsConsumed); await _innerStream.WriteAsync(scratchBytes, 0, bytesWritten, cancellationToken).ConfigureAwait(false); } while (!encoderFinished); } while (!decoderFinished); } finally { ArrayPool.Shared.Return(scratchChars); ArrayPool.Shared.Return(scratchBytes); } } } #else private ValueTask WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { EnsurePreWriteConditions(); if (cancellationToken.IsCancellationRequested) { #if NETSTANDARD return new ValueTask(Task.FromCanceled(cancellationToken)); #else // Lose track of the CancellationToken in this case. return new ValueTask(TaskHelpers.Canceled()); #endif } if (count == 0) { // ValueTask.CompletedTask return default; } return WriteAsyncCore(buffer, cancellationToken); async ValueTask WriteAsyncCore(ReadOnlyMemory remainingOuterEncodedBytes, CancellationToken cancellationToken) { int rentalLength = remainingOuterEncodedBytes.Length < MinWriteRentedArraySize ? MinWriteRentedArraySize : remainingOuterEncodedBytes.Length > MaxWriteRentedArraySize ? MaxWriteRentedArraySize : remainingOuterEncodedBytes.Length; char[] scratchChars = ArrayPool.Shared.Rent(rentalLength); byte[] scratchBytes = ArrayPool.Shared.Rent(rentalLength); try { bool decoderFinished, encoderFinished; do { // convert bytes [this] -> chars _thisDecoder.Convert( bytes: buffer, byteIndex: offset, byteCount: count, chars: scratchChars, charIndex: 0, charCount: rentalLength, flush: false, out int bytesConsumed, out int charsWritten, out decoderFinished); offset += bytesConsumed; count -= bytesConsumed; // convert chars -> bytes [inner] int scratchOffset = 0; do { _innerEncoder.Convert( chars: scratchChars, charIndex: scratchOffset, charCount: charsWritten, bytes: scratchBytes, byteIndex: 0, byteCount: rentalLength, flush: false, out int charsConsumed, out int bytesWritten, out encoderFinished); scratchOffset += charsConsumed; charsWritten -= charsConsumed; await _innerStream.WriteAsync(scratchBytes, 0, bytesWritten, cancellationToken).ConfigureAwait(false); } while (!encoderFinished); } while (!decoderFinished); } finally { ArrayPool.Shared.Return(scratchChars); ArrayPool.Shared.Return(scratchBytes); } } } #endif public override void WriteByte(byte value) { _singleByteBuffer[0] = value; Write(_singleByteBuffer, offset: 0, count: 1); } // From https://github.com/dotnet/runtime/blob/88868b7a781f4e5b9037b8721f30440207a7aa42/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ValidateBufferArguments(byte[] buffer, int offset, int count) { if (buffer is null) { throw Error.ArgumentNull(nameof(buffer)); } if (offset < 0) { throw Error.ArgumentMustBeGreaterThanOrEqualTo(nameof(offset), offset, minValue: 0); } if ((uint)count > buffer.Length - offset) { throw Error.ArgumentOutOfRange(nameof(count), count, Properties.Resources.Argument_InvalidOffLen); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Internal/TypeExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Reflection; namespace System.Net.Http { internal static class TypeExtensions { #if NETSTANDARD1_3 private static bool EqualTo(this Type[] t1, Type[] t2) { if (t1.Length != t2.Length) { return false; } for (int idx = 0; idx < t1.Length; ++idx) { if (t1[idx] != t2[idx]) { return false; } } return true; } public static ConstructorInfo GetConstructor(this Type type, Type[] types) { return type.GetTypeInfo().DeclaredConstructors .Where(c => c.IsPublic) .SingleOrDefault(c => c.GetParameters() .Select(p => p.ParameterType).ToArray().EqualTo(types)); } #endif public static Type ExtractGenericInterface(this Type queryType, Type interfaceType) { Func matchesInterface = t => t.IsGenericType() && t.GetGenericTypeDefinition() == interfaceType; return (matchesInterface(queryType)) ? queryType : queryType.GetInterfaces().FirstOrDefault(matchesInterface); } #if NETSTANDARD1_3 public static Type[] GetGenericArguments(this Type type) { return type.GetTypeInfo().GenericTypeArguments; } public static Type[] GetInterfaces(this Type type) { return type.GetTypeInfo().ImplementedInterfaces.ToArray(); } #endif #if NETSTANDARD1_3 public static bool IsAssignableFrom(this Type type, Type c) { return type.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo()); } #endif public static bool IsGenericType(this Type type) { #if NETSTANDARD1_3 return type.GetTypeInfo().IsGenericType; #else return type.IsGenericType; #endif } public static bool IsInterface(this Type type) { #if NETSTANDARD1_3 return type.GetTypeInfo().IsInterface; #else return type.IsInterface; #endif } public static bool IsValueType(this Type type) { #if NETSTANDARD1_3 return type.GetTypeInfo().IsValueType; #else return type.IsValueType; #endif } } } ================================================ FILE: src/System.Net.Http.Formatting/InvalidByteRangeException.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Runtime.Serialization; using System.Web.Http; namespace System.Net.Http { /// /// An exception thrown by in case none of the requested ranges /// overlap with the current extend of the selected resource. The current extend of the resource /// is indicated in the ContentRange property. /// [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "Exception is not intended to be serialized.")] [SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly", Justification = "Exception is not intended to be serialized.")] [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "The ContentRange is a required parameter.")] public class InvalidByteRangeException : Exception { public InvalidByteRangeException(ContentRangeHeaderValue contentRange) { Initialize(contentRange); } public InvalidByteRangeException(ContentRangeHeaderValue contentRange, string message) : base(message) { Initialize(contentRange); } public InvalidByteRangeException(ContentRangeHeaderValue contentRange, string message, Exception innerException) : base(message, innerException) { Initialize(contentRange); } #if !NETSTANDARD1_3 // Exception is not serializable in netstandard1.3. public InvalidByteRangeException(ContentRangeHeaderValue contentRange, SerializationInfo info, StreamingContext context) : base(info, context) { Initialize(contentRange); } #endif /// /// The current extend of the resource indicated in terms of a ContentRange header field. /// public ContentRangeHeaderValue ContentRange { get; private set; } private void Initialize(ContentRangeHeaderValue contentRange) { if (contentRange == null) { throw Error.ArgumentNull("contentRange"); } ContentRange = contentRange; } } } ================================================ FILE: src/System.Net.Http.Formatting/MimeBodyPart.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Maintains information about MIME body parts parsed by . /// internal class MimeBodyPart : IDisposable { private static readonly Type _streamType = typeof(Stream); private Stream _outputStream; private MultipartStreamProvider _streamProvider; private HttpContent _parentContent; private HttpContent _content; private HttpContentHeaders _headers; /// /// Initializes a new instance of the class. /// /// The stream provider. /// The max length of the MIME header within each MIME body part. /// The part's parent content public MimeBodyPart(MultipartStreamProvider streamProvider, int maxBodyPartHeaderSize, HttpContent parentContent) { Contract.Assert(streamProvider != null); Contract.Assert(parentContent != null); _streamProvider = streamProvider; _parentContent = parentContent; Segments = new List>(2); _headers = FormattingUtilities.CreateEmptyContentHeaders(); HeaderParser = new InternetMessageFormatHeaderParser( _headers, maxBodyPartHeaderSize, ignoreHeaderValidation: true); } /// /// Gets the header parser. /// /// /// The header parser. /// public InternetMessageFormatHeaderParser HeaderParser { get; private set; } /// /// Gets the part's content as an HttpContent. /// /// /// The part's content, or null if the part had no content. /// public HttpContent GetCompletedHttpContent() { Contract.Assert(IsComplete); if (_content == null) { return null; } _headers.CopyTo(_content.Headers); return _content; } /// /// Gets the set of pointing to the read buffer with /// contents of this body part. /// public List> Segments { get; private set; } /// /// Gets or sets a value indicating whether the body part has been completed. /// /// /// true if this instance is complete; otherwise, false. /// public bool IsComplete { get; set; } /// /// Gets or sets a value indicating whether this is the final body part. /// /// /// true if this instance is complete; otherwise, false. /// public bool IsFinal { get; set; } /// /// Writes the into the part's output stream. /// /// The current segment to be written to the part's output stream. /// The token to monitor for cancellation requests. public async Task WriteSegment(ArraySegment segment, CancellationToken cancellationToken) { var stream = GetOutputStream(); await stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken); } /// /// Gets the output stream. /// /// The output stream to write the body part to. private Stream GetOutputStream() { if (_outputStream == null) { try { _outputStream = _streamProvider.GetStream(_parentContent, _headers); } catch (Exception e) { throw Error.InvalidOperation(e, Properties.Resources.ReadAsMimeMultipartStreamProviderException, _streamProvider.GetType().Name); } if (_outputStream == null) { throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartStreamProviderNull, _streamProvider.GetType().Name, _streamType.Name); } if (!_outputStream.CanWrite) { throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartStreamProviderReadOnly, _streamProvider.GetType().Name, _streamType.Name); } _content = new StreamContent(_outputStream); } return _outputStream; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool disposing) { if (disposing) { CleanupOutputStream(); CleanupHttpContent(); _parentContent = null; HeaderParser = null; Segments.Clear(); } } /// /// In the success case, the HttpContent is to be used after this Part has been parsed and disposed of. /// Only if Dispose has been called on a non-completed part, the parsed HttpContent needs to be disposed of as well. /// private void CleanupHttpContent() { if (!IsComplete && _content != null) { _content.Dispose(); } _content = null; } /// /// Resets the output stream by either closing it or, in the case of a resetting /// position to 0 so that it can be read by the caller. /// private void CleanupOutputStream() { if (_outputStream != null) { MemoryStream output = _outputStream as MemoryStream; if (output != null) { output.Position = 0; } else { #if NETSTANDARD1_3 _outputStream.Dispose(); #else _outputStream.Close(); #endif } _outputStream = null; } } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartFileData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { public class MultipartFileData { public MultipartFileData(HttpContentHeaders headers, string localFileName) { if (headers == null) { throw Error.ArgumentNull("headers"); } if (localFileName == null) { throw Error.ArgumentNull("localFileName"); } Headers = headers; LocalFileName = localFileName; } public HttpContentHeaders Headers { get; private set; } public string LocalFileName { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartFileStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Net.Http.Headers; using System.Net.Http.Internal; using System.Web.Http; namespace System.Net.Http { /// /// A suited for writing each MIME body parts of the MIME multipart /// message to a file using a . /// public class MultipartFileStreamProvider : MultipartStreamProvider { private const int MinBufferSize = 1; private const int DefaultBufferSize = 0x1000; private string _rootPath; private int _bufferSize = DefaultBufferSize; private Collection _fileData = new Collection(); /// /// Initializes a new instance of the class. /// /// The root path where the content of MIME multipart body parts are written to. public MultipartFileStreamProvider(string rootPath) : this(rootPath, DefaultBufferSize) { } /// /// Initializes a new instance of the class. /// /// The root path where the content of MIME multipart body parts are written to. /// The number of bytes buffered for writes to a file. public MultipartFileStreamProvider(string rootPath, int bufferSize) { if (rootPath == null) { throw Error.ArgumentNull("rootPath"); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } _rootPath = Path.GetFullPath(rootPath); _bufferSize = bufferSize; } /// /// Gets a collection containing the local files names and associated HTTP content headers of MIME /// body parts written to file. /// public Collection FileData { get { return _fileData; } } /// /// Gets the root path where the content of MIME multipart body parts are written to. /// protected string RootPath { get { return _rootPath; } } /// /// Gets the number of bytes buffered for writes to a file. /// protected int BufferSize { get { return _bufferSize; } } [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Stream is closed by caller (MultipartWriteDelegatingStream is just a wrapper that calls into the inner stream.)")] public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } string localFilePath; try { string filename = GetLocalFileName(headers); localFilePath = Path.Combine(_rootPath, Path.GetFileName(filename)); } catch (Exception e) { throw Error.InvalidOperation(e, Properties.Resources.MultipartStreamProviderInvalidLocalFileName); } // Add local file name MultipartFileData fileData = new MultipartFileData(headers, localFilePath); _fileData.Add(fileData); return File.Create(localFilePath, _bufferSize, FileOptions.Asynchronous); } /// /// Gets the name of the local file which will be combined with the root path to /// create an absolute file name where the contents of the current MIME body part /// will be stored. /// /// The headers for the current MIME body part. /// A relative filename with no path component. public virtual string GetLocalFileName(HttpContentHeaders headers) { if (headers == null) { throw Error.ArgumentNull("headers"); } return String.Format(CultureInfo.InvariantCulture, "BodyPart_{0}", Guid.NewGuid()); } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartFormDataRemoteStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; using System.Net.Http.Formatting.Internal; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// A implementation suited for use with HTML file uploads for writing file /// content to a remote storage . The stream provider looks at the Content-Disposition /// header field and determines an output remote based on the presence of a filename /// parameter. If a filename parameter is present in the Content-Disposition header field, then the /// body part is written to a remote provided by . /// Otherwise it is written to a . /// public abstract class MultipartFormDataRemoteStreamProvider : MultipartStreamProvider { private CancellationToken _cancellationToken = CancellationToken.None; /// /// Initializes a new instance of the class. /// protected MultipartFormDataRemoteStreamProvider() { FormData = HttpValueCollection.Create(); FileData = new Collection(); } /// /// Gets a collection of file data passed as part of the multipart form data. /// public Collection FileData { get; private set; } /// /// Gets a of form data passed as part of the multipart form data. /// public NameValueCollection FormData { get; private set; } /// /// Provides a for . Override this method to provide a /// remote stream to which the data should be written. /// /// The parent MIME multipart instance. /// The header fields describing the body part's content. /// /// A result specifying a remote stream where the file will be written to and a location where the file can be /// accessed. It cannot be null and the stream must be writable. /// public abstract RemoteStreamInfo GetRemoteStream(HttpContent parent, HttpContentHeaders headers); /// public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (MultipartFormDataStreamProviderHelper.IsFileContent(parent, headers)) { RemoteStreamInfo remoteStreamInfo = GetRemoteStream(parent, headers); if (remoteStreamInfo == null) { throw Error.InvalidOperation(Properties.Resources.RemoteStreamInfoCannotBeNull, "GetRemoteStream", GetType().Name); } FileData.Add(new MultipartRemoteFileData(headers, remoteStreamInfo.Location, remoteStreamInfo.FileName)); return remoteStreamInfo.RemoteStream; } return new MemoryStream(); } /// /// Read the non-file contents as form data. /// /// A representing the post processing. public override Task ExecutePostProcessingAsync() { // In consistency with existing MultipartFormDataStreamProvider, // this method predates support for cancellation, and we need to make sure it is always invoked when // ExecutePostProcessingAsync is called for compatability. return MultipartFormDataStreamProviderHelper.ReadFormDataAsync(Contents, FormData, _cancellationToken); } /// /// Read the non-file contents as form data. /// /// The token to monitor for cancellation requests. /// A representing the post processing. public override Task ExecutePostProcessingAsync(CancellationToken cancellationToken) { _cancellationToken = cancellationToken; return ExecutePostProcessingAsync(); } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartFormDataStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.IO; using System.Net.Http.Formatting.Internal; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; namespace System.Net.Http { /// /// A implementation suited for use with HTML file uploads for writing file /// content to a . The stream provider looks at the Content-Disposition header /// field and determines an output based on the presence of a filename parameter. /// If a filename parameter is present in the Content-Disposition header field then the body /// part is written to a , otherwise it is written to a . /// This makes it convenient to process MIME Multipart HTML Form data which is a combination of form /// data and file content. /// public class MultipartFormDataStreamProvider : MultipartFileStreamProvider { // pass around cancellation token through field to maintain backward compat. private CancellationToken _cancellationToken; /// /// Initializes a new instance of the class. /// /// The root path where the content of MIME multipart body parts are written to. public MultipartFormDataStreamProvider(string rootPath) : base(rootPath) { FormData = HttpValueCollection.Create(); } /// /// Initializes a new instance of the class. /// /// The root path where the content of MIME multipart body parts are written to. /// The number of bytes buffered for writes to the file. public MultipartFormDataStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize) { FormData = HttpValueCollection.Create(); } /// /// Gets a of form data passed as part of the multipart form data. /// public NameValueCollection FormData { get; private set; } /// /// This body part stream provider examines the headers provided by the MIME multipart parser /// and decides whether it should return a file stream or a memory stream for the body part to be /// written to. /// /// The parent MIME multipart HttpContent instance. /// Header fields describing the body part /// The instance where the message body part is written to. public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (MultipartFormDataStreamProviderHelper.IsFileContent(parent, headers)) { return base.GetStream(parent, headers); } return new MemoryStream(); } /// /// Read the non-file contents as form data. /// /// A representing the post processing. public override Task ExecutePostProcessingAsync() { // This method predates support for cancellation, and we need to make sure it is always invoked when // ExecutePostProcessingAsync is called for compatability. return MultipartFormDataStreamProviderHelper.ReadFormDataAsync(Contents, FormData, _cancellationToken); } /// /// Read the non-file contents as form data. /// /// The token to monitor for cancellation requests. /// A representing the post processing. public override Task ExecutePostProcessingAsync(CancellationToken cancellationToken) { _cancellationToken = cancellationToken; return ExecutePostProcessingAsync(); } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartFormDataStreamProviderHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { internal static class MultipartFormDataStreamProviderHelper { public static bool IsFileContent(HttpContent parent, HttpContentHeaders headers) { if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } // For form data, Content-Disposition header is a requirement. ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition; if (contentDisposition == null) { // If no Content-Disposition header was present. throw Error.InvalidOperation(Properties.Resources.MultipartFormDataStreamProviderNoContentDisposition, "Content-Disposition"); } // The file name's existence indicates it is a file data. if (!String.IsNullOrEmpty(contentDisposition.FileName)) { return true; } return false; } /// /// Read the non-file contents as form data. /// /// A representing the post processing. public static async Task ReadFormDataAsync(Collection contents, NameValueCollection formData, CancellationToken cancellationToken) { // Find instances of HttpContent for which we created a memory stream and read them asynchronously // to get the string content and then add that as form data foreach (HttpContent content in contents) { ContentDispositionHeaderValue contentDisposition = content.Headers.ContentDisposition; // If FileName is null or empty, the content is form data and will be processed. if (String.IsNullOrEmpty(contentDisposition.FileName)) { // Extract name from Content-Disposition header. We know from earlier that the header is present. string formFieldName = FormattingUtilities.UnquoteToken(contentDisposition.Name) ?? String.Empty; // Read the contents as string data and add to form data cancellationToken.ThrowIfCancellationRequested(); string formFieldValue = await content.ReadAsStringAsync(); formData.Add(formFieldName, formFieldValue); } } } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartMemoryStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// Provides a implementation that returns a instance. /// This facilitates deserialization or other manipulation of the contents in memory. /// public class MultipartMemoryStreamProvider : MultipartStreamProvider { /// /// This implementation returns a instance. /// This facilitates deserialization or other manipulation of the contents in memory. /// public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } return new MemoryStream(); } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartRelatedStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// An suited for reading MIME body parts following the /// multipart/related media type as defined in RFC 2387 (see http://www.ietf.org/rfc/rfc2387.txt). /// public class MultipartRelatedStreamProvider : MultipartStreamProvider { private const string RelatedSubType = "related"; private const string ContentID = "Content-ID"; private const string StartParameter = "Start"; private HttpContent _rootContent; private HttpContent _parent; /// /// Gets the instance that has been marked as the root content in the /// MIME multipart related message using the start parameter. If no start parameter is /// present then pick the first of the children. /// public HttpContent RootContent { get { if (_rootContent == null) { _rootContent = FindRootContent(_parent, Contents); } return _rootContent; } } public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { if (parent == null) { throw Error.ArgumentNull("parent"); } if (headers == null) { throw Error.ArgumentNull("headers"); } // See if we need to remember the parent to be able to determine the root content if (_parent == null) { _parent = parent; } return new MemoryStream(); } /// /// Looks for the "start" parameter of the parent's content type and then finds the corresponding /// child HttpContent with a matching Content-ID header field. /// /// The matching child or null if none found. private static HttpContent FindRootContent(HttpContent parent, IEnumerable children) { Contract.Assert(children != null); // Find 'start' parameter from parent content type. The value is used // to identify the MIME body with the corresponding Content-ID header value. NameValueHeaderValue startNameValue = FindMultipartRelatedParameter(parent, StartParameter); if (startNameValue == null) { // If we didn't find a "start" parameter then take the first child. return children.FirstOrDefault(); } // Look for the child with a Content-ID header that corresponds to the "start" value. // If no matching child is found then we return null. string startValue = FormattingUtilities.UnquoteToken(startNameValue.Value); return children.FirstOrDefault( content => { IEnumerable values; if (content.Headers.TryGetValues(ContentID, out values)) { return String.Equals( FormattingUtilities.UnquoteToken(values.ElementAt(0)), startValue, StringComparison.OrdinalIgnoreCase); } return false; }); } /// /// Looks for a parameter in the . /// /// The matching parameter or null if none found. private static NameValueHeaderValue FindMultipartRelatedParameter(HttpContent content, string parameterName) { // If no parent then we are done if (content == null) { return null; } // Check that we have a parent content type and that it is indeed multipart/related MediaTypeHeaderValue parentContentType = content.Headers.ContentType; if (parentContentType == null || !content.IsMimeMultipartContent(RelatedSubType)) { return null; } // Look for parameter return parentContentType.Parameters.FirstOrDefault(nvp => String.Equals(nvp.Name, parameterName, StringComparison.OrdinalIgnoreCase)); } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartRemoteFileData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// Represents a multipart file data for remote storage. /// public class MultipartRemoteFileData { /// /// Initializes a new instance of the class. /// /// The headers of the multipart file data. /// The remote file's location. /// The remote file's name. public MultipartRemoteFileData(HttpContentHeaders headers, string location, string fileName) { if (headers == null) { throw Error.ArgumentNull("headers"); } if (location == null) { throw Error.ArgumentNull("location"); } if (fileName == null) { throw Error.ArgumentNull("fileName"); } FileName = fileName; Headers = headers; Location = location; } /// /// Gets the remote file's name. /// public string FileName { get; private set; } /// /// Gets the headers of the multipart file data. /// public HttpContentHeaders Headers { get; private set; } /// /// Gets the remote file's location. /// public string Location { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/MultipartStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.IO; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; namespace System.Net.Http { /// /// An implementation examines the headers provided by the MIME multipart parser /// as part of the MIME multipart extension methods (see ) and decides /// what kind of stream to return for the body part to be written to. /// public abstract class MultipartStreamProvider { private Collection _contents = new Collection(); /// /// Initializes a new instance of the class. /// protected MultipartStreamProvider() { } /// /// Gets the collection of instances where each instance represents a MIME body part. /// public Collection Contents { get { return _contents; } } /// /// When a MIME multipart body part has been parsed this method is called to get a stream for where to write the body part to. /// /// The parent MIME multipart instance. /// The header fields describing the body parts content. Looking for header fields such as /// Content-Type and Content-Disposition can help provide the appropriate stream. In addition to using the information /// in the provided header fields, it is also possible to add new header fields or modify existing header fields. This can /// be useful to get around situations where the Content-type may say application/octet-stream but based on /// analyzing the Content-Disposition header field it is found that the content in fact is application/json, for example. /// A stream instance where the contents of a body part will be written to. public abstract Stream GetStream(HttpContent parent, HttpContentHeaders headers); /// /// Immediately upon reading the last MIME body part but before completing the read task, this method is /// called to enable the to do any post processing on the /// instances that have been read. For example, it can be used to copy the data to another location, or perform /// some other kind of post processing on the data before completing the read operation. /// /// A representing the post processing. public virtual Task ExecutePostProcessingAsync() { return TaskHelpers.Completed(); } /// /// Immediately upon reading the last MIME body part but before completing the read task, this method is /// called to enable the to do any post processing on the /// instances that have been read. For example, it can be used to copy the data to another location, or perform /// some other kind of post processing on the data before completing the read operation. /// /// The token to monitor for cancellation requests. /// A representing the post processing. public virtual Task ExecutePostProcessingAsync(CancellationToken cancellationToken) { // Call the other overload to maintain backward compatibility. return ExecutePostProcessingAsync(); } } } ================================================ FILE: src/System.Net.Http.Formatting/ObjectContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Contains a value as well as an associated that will be /// used to serialize the value when writing this content. /// public class ObjectContent : HttpContent { private object _value; private readonly MediaTypeFormatter _formatter; /// /// Initializes a new instance of the class. /// /// The type of object this instance will contain. /// The value of the object this instance will contain. /// The formatter to use when serializing the value. public ObjectContent(Type type, object value, MediaTypeFormatter formatter) : this(type, value, formatter, (MediaTypeHeaderValue)null) { } /// /// Initializes a new instance of the class. /// /// The type of object this instance will contain. /// The value of the object this instance will contain. /// The formatter to use when serializing the value. /// The authoritative value of the content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. public ObjectContent(Type type, object value, MediaTypeFormatter formatter, string mediaType) : this(type, value, formatter, BuildHeaderValue(mediaType)) { } /// /// Initializes a new instance of the class. /// /// The type of object this instance will contain. /// The value of the object this instance will contain. /// The formatter to use when serializing the value. /// The authoritative value of the content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. public ObjectContent(Type type, object value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType) { if (type == null) { throw Error.ArgumentNull("type"); } if (formatter == null) { throw Error.ArgumentNull("formatter"); } if (!formatter.CanWriteType(type)) { throw Error.InvalidOperation(Properties.Resources.ObjectContent_FormatterCannotWriteType, formatter.GetType().FullName, type.Name); } _formatter = formatter; ObjectType = type; VerifyAndSetObject(value); _formatter.SetDefaultContentHeaders(type, Headers, mediaType); } /// /// Gets the type of object managed by this instance. /// public Type ObjectType { get; private set; } /// /// The formatter associated with this content instance. /// public MediaTypeFormatter Formatter { get { return _formatter; } } /// /// Gets or sets the value of the current . /// public object Value { get { return _value; } set { _value = value; } } internal static MediaTypeHeaderValue BuildHeaderValue(string mediaType) { return mediaType != null ? new MediaTypeHeaderValue(mediaType) : null; } /// /// Asynchronously serializes the object's content to the given . /// /// The to which to write. /// The associated . /// A instance that is asynchronously serializing the object's content. protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { return _formatter.WriteToStreamAsync(ObjectType, Value, stream, this, context); } /// /// Computes the length of the stream if possible. /// /// The computed length of the stream. /// true if the length has been computed; otherwise false. protected override bool TryComputeLength(out long length) { length = -1; return false; } private static bool IsTypeNullable(Type type) { return !type.IsValueType() || (type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(Nullable<>)); } private void VerifyAndSetObject(object value) { Contract.Assert(ObjectType != null, "Type cannot be null"); if (value == null) { // Null may not be assigned to value types (unless Nullable) if (!IsTypeNullable(ObjectType)) { throw Error.InvalidOperation(Properties.Resources.CannotUseNullValueType, typeof(ObjectContent).Name, ObjectType.Name); } } else { // Non-null objects must be a type assignable to Type Type objectType = value.GetType(); if (!ObjectType.IsAssignableFrom(objectType)) { throw Error.Argument("value", Properties.Resources.ObjectAndTypeDisagree, objectType.Name, ObjectType.Name); } } _value = value; } } } ================================================ FILE: src/System.Net.Http.Formatting/ObjectContentOfT.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting; using System.Net.Http.Headers; namespace System.Net.Http { /// /// Generic form of . /// /// The type of object this class will contain. public class ObjectContent : ObjectContent { /// /// Initializes a new instance of the class. /// /// The value of the object this instance will contain. /// The formatter to use when serializing the value. public ObjectContent(T value, MediaTypeFormatter formatter) : this(value, formatter, (MediaTypeHeaderValue)null) { } /// /// Initializes a new instance of the class. /// /// The value of the object this instance will contain. /// The formatter to use when serializing the value. /// The authoritative value of the content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. public ObjectContent(T value, MediaTypeFormatter formatter, string mediaType) : this(value, formatter, BuildHeaderValue(mediaType)) { } /// /// Initializes a new instance of the class. /// /// The value of the object this instance will contain. /// The formatter to use when serializing the value. /// The authoritative value of the content's Content-Type header. Can be null in which case the /// formatter's default content type will be used. public ObjectContent(T value, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType) : base(typeof(T), value, formatter, mediaType) { } } } ================================================ FILE: src/System.Net.Http.Formatting/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("System.Net.Http.Formatting")] [assembly: AssemblyDescription("")] [assembly: Guid("7fa1ae84-36e2-46b6-812c-c985a8e65e9a")] #if NETSTANDARD2_0 [assembly: InternalsVisibleTo("System.Net.Http.Formatting.ns2_0.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] #elif NETSTANDARD1_3 [assembly: InternalsVisibleTo("Microsoft.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("System.Net.Http.Formatting.ns1_3.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] #else [assembly: InternalsVisibleTo("System.Net.Http.Formatting.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("System.Net.Http.Formatting.Test.Integration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] #endif ================================================ FILE: src/System.Net.Http.Formatting/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Net.Http.Properties { using System; using System.Reflection; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { #if NETSTANDARD1_3 var assembly = typeof(Resources).GetTypeInfo().Assembly; #else var assembly = typeof(Resources).Assembly; #endif global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Net.Http.Properties.Resources", assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.. /// internal static string Argument_InvalidOffLen { get { return ResourceManager.GetString("Argument_InvalidOffLen", resourceCulture); } } /// /// Looks up a localized string similar to Async Callback threw an exception.. /// internal static string AsyncResult_CallbackThrewException { get { return ResourceManager.GetString("AsyncResult_CallbackThrewException", resourceCulture); } } /// /// Looks up a localized string similar to The IAsyncResult implementation '{0}' tried to complete a single operation multiple times. This could be caused by an incorrect application IAsyncResult implementation or other extensibility code, such as an IAsyncResult that returns incorrect CompletedSynchronously values or invokes the AsyncCallback multiple times.. /// internal static string AsyncResult_MultipleCompletes { get { return ResourceManager.GetString("AsyncResult_MultipleCompletes", resourceCulture); } } /// /// Looks up a localized string similar to End cannot be called twice on an AsyncResult.. /// internal static string AsyncResult_MultipleEnds { get { return ResourceManager.GetString("AsyncResult_MultipleEnds", resourceCulture); } } /// /// Looks up a localized string similar to An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'.. /// internal static string AsyncResult_ResultMismatch { get { return ResourceManager.GetString("AsyncResult_ResultMismatch", resourceCulture); } } /// /// Looks up a localized string similar to Found zero byte ranges. There must be at least one byte range provided.. /// internal static string ByteRangeStreamContentNoRanges { get { return ResourceManager.GetString("ByteRangeStreamContentNoRanges", resourceCulture); } } /// /// Looks up a localized string similar to The range unit '{0}' is not valid. The range must have a unit of '{1}'.. /// internal static string ByteRangeStreamContentNotBytesRange { get { return ResourceManager.GetString("ByteRangeStreamContentNotBytesRange", resourceCulture); } } /// /// Looks up a localized string similar to The stream over which '{0}' provides a range view must have a length greater than or equal to 1.. /// internal static string ByteRangeStreamEmpty { get { return ResourceManager.GetString("ByteRangeStreamEmpty", resourceCulture); } } /// /// Looks up a localized string similar to The 'From' value of the range must be less than or equal to {0}.. /// internal static string ByteRangeStreamInvalidFrom { get { return ResourceManager.GetString("ByteRangeStreamInvalidFrom", resourceCulture); } } /// /// Looks up a localized string similar to An attempt was made to move the position before the beginning of the stream.. /// internal static string ByteRangeStreamInvalidOffset { get { return ResourceManager.GetString("ByteRangeStreamInvalidOffset", resourceCulture); } } /// /// Looks up a localized string similar to None of the requested ranges ({0}) overlap with the current extent of the selected resource.. /// internal static string ByteRangeStreamNoneOverlap { get { return ResourceManager.GetString("ByteRangeStreamNoneOverlap", resourceCulture); } } /// /// Looks up a localized string similar to The requested range ({0}) does not overlap with the current extent of the selected resource.. /// internal static string ByteRangeStreamNoOverlap { get { return ResourceManager.GetString("ByteRangeStreamNoOverlap", resourceCulture); } } /// /// Looks up a localized string similar to The stream over which '{0}' provides a range view must be seekable.. /// internal static string ByteRangeStreamNotSeekable { get { return ResourceManager.GetString("ByteRangeStreamNotSeekable", resourceCulture); } } /// /// Looks up a localized string similar to This is a read-only stream.. /// internal static string ByteRangeStreamReadOnly { get { return ResourceManager.GetString("ByteRangeStreamReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to A null '{0}' is not valid.. /// internal static string CannotHaveNullInList { get { return ResourceManager.GetString("CannotHaveNullInList", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' of '{1}' cannot be used as a supported media type because it is a media range.. /// internal static string CannotUseMediaRangeForSupportedMediaType { get { return ResourceManager.GetString("CannotUseMediaRangeForSupportedMediaType", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' type cannot accept a null value for the value type '{1}'.. /// internal static string CannotUseNullValueType { get { return ResourceManager.GetString("CannotUseNullValueType", resourceCulture); } } /// /// Looks up a localized string similar to The specified value is not a valid cookie name.. /// internal static string CookieInvalidName { get { return ResourceManager.GetString("CookieInvalidName", resourceCulture); } } /// /// Looks up a localized string similar to Cookie cannot be null.. /// internal static string CookieNull { get { return ResourceManager.GetString("CookieNull", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' list is invalid because it contains one or more null items.. /// internal static string DelegatingHandlerArrayContainsNullItem { get { return ResourceManager.GetString("DelegatingHandlerArrayContainsNullItem", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' list is invalid because the property '{1}' of '{2}' is not null.. /// internal static string DelegatingHandlerArrayHasNonNullInnerHandler { get { return ResourceManager.GetString("DelegatingHandlerArrayHasNonNullInnerHandler", resourceCulture); } } /// /// Looks up a localized string similar to Error reading HTML form URL-encoded data stream.. /// internal static string ErrorReadingFormUrlEncodedStream { get { return ResourceManager.GetString("ErrorReadingFormUrlEncodedStream", resourceCulture); } } /// /// Looks up a localized string similar to Mismatched types at node '{0}'.. /// internal static string FormUrlEncodedMismatchingTypes { get { return ResourceManager.GetString("FormUrlEncodedMismatchingTypes", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing HTML form URL-encoded data, byte {0}.. /// internal static string FormUrlEncodedParseError { get { return ResourceManager.GetString("FormUrlEncodedParseError", resourceCulture); } } /// /// Looks up a localized string similar to Invalid HTTP status code: '{0}'. The status code must be between {1} and {2}.. /// internal static string HttpInvalidStatusCode { get { return ResourceManager.GetString("HttpInvalidStatusCode", resourceCulture); } } /// /// Looks up a localized string similar to Invalid HTTP version: '{0}'. The version must start with the characters '{1}'.. /// internal static string HttpInvalidVersion { get { return ResourceManager.GetString("HttpInvalidVersion", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' of the '{1}' has already been read.. /// internal static string HttpMessageContentAlreadyRead { get { return ResourceManager.GetString("HttpMessageContentAlreadyRead", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' must be seekable in order to create an '{1}' instance containing an entity body. . /// internal static string HttpMessageContentStreamMustBeSeekable { get { return ResourceManager.GetString("HttpMessageContentStreamMustBeSeekable", resourceCulture); } } /// /// Looks up a localized string similar to Error reading HTTP message.. /// internal static string HttpMessageErrorReading { get { return ResourceManager.GetString("HttpMessageErrorReading", resourceCulture); } } /// /// Looks up a localized string similar to Invalid '{0}' instance provided. It does not have a content type header with a value of '{1}'.. /// internal static string HttpMessageInvalidMediaType { get { return ResourceManager.GetString("HttpMessageInvalidMediaType", resourceCulture); } } /// /// Looks up a localized string similar to HTTP Request URI cannot be an empty string.. /// internal static string HttpMessageParserEmptyUri { get { return ResourceManager.GetString("HttpMessageParserEmptyUri", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing HTTP message header byte {0} of message {1}.. /// internal static string HttpMessageParserError { get { return ResourceManager.GetString("HttpMessageParserError", resourceCulture); } } /// /// Looks up a localized string similar to An invalid number of '{0}' header fields were present in the HTTP Request. It must contain exactly one '{0}' header field but found {1}.. /// internal static string HttpMessageParserInvalidHostCount { get { return ResourceManager.GetString("HttpMessageParserInvalidHostCount", resourceCulture); } } /// /// Looks up a localized string similar to Invalid URI scheme: '{0}'. The URI scheme must be a valid '{1}' scheme.. /// internal static string HttpMessageParserInvalidUriScheme { get { return ResourceManager.GetString("HttpMessageParserInvalidUriScheme", resourceCulture); } } /// /// Looks up a localized string similar to Invalid array at node '{0}'.. /// internal static string InvalidArrayInsert { get { return ResourceManager.GetString("InvalidArrayInsert", resourceCulture); } } /// /// Looks up a localized string similar to Traditional style array without '[]' is not supported with nested object at location {0}.. /// internal static string JQuery13CompatModeNotSupportNestedJson { get { return ResourceManager.GetString("JQuery13CompatModeNotSupportNestedJson", resourceCulture); } } /// /// Looks up a localized string similar to Unable to validate types on this platform when {0} is 'true'. Please reset {0} or move to a supported platform, one where the 'netstandard2.0' assembly is usable.. /// internal static string JsonMediaTypeFormatter_DCS_NotSupported { get { return ResourceManager.GetString("JsonMediaTypeFormatter_DCS_NotSupported", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' method returned null. It must return a JSON serializer instance.. /// internal static string JsonSerializerFactoryReturnedNull { get { return ResourceManager.GetString("JsonSerializerFactoryReturnedNull", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' method threw an exception when attempting to create a JSON serializer.. /// internal static string JsonSerializerFactoryThrew { get { return ResourceManager.GetString("JsonSerializerFactoryThrew", resourceCulture); } } /// /// Looks up a localized string similar to The maximum read depth ({0}) has been exceeded because the form url-encoded data being read has more levels of nesting than is allowed.. /// internal static string MaxDepthExceeded { get { return ResourceManager.GetString("MaxDepthExceeded", resourceCulture); } } /// /// Looks up a localized string similar to The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.. /// internal static string MaxHttpCollectionKeyLimitReached { get { return ResourceManager.GetString("MaxHttpCollectionKeyLimitReached", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing BSON data; unable to read content as a {0}.. /// internal static string MediaTypeFormatter_BsonParseError_MissingData { get { return ResourceManager.GetString("MediaTypeFormatter_BsonParseError_MissingData", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing BSON data; unexpected dictionary content: {0} entries, first key '{1}'.. /// internal static string MediaTypeFormatter_BsonParseError_UnexpectedData { get { return ResourceManager.GetString("MediaTypeFormatter_BsonParseError_UnexpectedData", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' method returned null. It must return a JSON reader instance.. /// internal static string MediaTypeFormatter_JsonReaderFactoryReturnedNull { get { return ResourceManager.GetString("MediaTypeFormatter_JsonReaderFactoryReturnedNull", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' method returned null. It must return a JSON writer instance.. /// internal static string MediaTypeFormatter_JsonWriterFactoryReturnedNull { get { return ResourceManager.GetString("MediaTypeFormatter_JsonWriterFactoryReturnedNull", resourceCulture); } } /// /// Looks up a localized string similar to The media type formatter of type '{0}' does not support reading because it does not implement the ReadFromStreamAsync method.. /// internal static string MediaTypeFormatterCannotRead { get { return ResourceManager.GetString("MediaTypeFormatterCannotRead", resourceCulture); } } /// /// Looks up a localized string similar to The media type formatter of type '{0}' does not support reading because it does not implement the ReadFromStream method.. /// internal static string MediaTypeFormatterCannotReadSync { get { return ResourceManager.GetString("MediaTypeFormatterCannotReadSync", resourceCulture); } } /// /// Looks up a localized string similar to The media type formatter of type '{0}' does not support writing because it does not implement the WriteToStreamAsync method.. /// internal static string MediaTypeFormatterCannotWrite { get { return ResourceManager.GetString("MediaTypeFormatterCannotWrite", resourceCulture); } } /// /// Looks up a localized string similar to The media type formatter of type '{0}' does not support writing because it does not implement the WriteToStream method.. /// internal static string MediaTypeFormatterCannotWriteSync { get { return ResourceManager.GetString("MediaTypeFormatterCannotWriteSync", resourceCulture); } } /// /// Looks up a localized string similar to No encoding found for media type formatter '{0}'. There must be at least one supported encoding registered in order for the media type formatter to read or write content.. /// internal static string MediaTypeFormatterNoEncoding { get { return ResourceManager.GetString("MediaTypeFormatterNoEncoding", resourceCulture); } } /// /// Looks up a localized string similar to MIME multipart boundary cannot end with an empty space.. /// internal static string MimeMultipartParserBadBoundary { get { return ResourceManager.GetString("MimeMultipartParserBadBoundary", resourceCulture); } } /// /// Looks up a localized string similar to Did not find required '{0}' header field in MIME multipart body part.. /// internal static string MultipartFormDataStreamProviderNoContentDisposition { get { return ResourceManager.GetString("MultipartFormDataStreamProviderNoContentDisposition", resourceCulture); } } /// /// Looks up a localized string similar to Could not determine a valid local file name for the multipart body part.. /// internal static string MultipartStreamProviderInvalidLocalFileName { get { return ResourceManager.GetString("MultipartStreamProviderInvalidLocalFileName", resourceCulture); } } /// /// Looks up a localized string similar to Nested bracket is not valid for '{0}' data at position {1}.. /// internal static string NestedBracketNotValid { get { return ResourceManager.GetString("NestedBracketNotValid", resourceCulture); } } /// /// Looks up a localized string similar to A non-null request URI must be provided to determine if a '{0}' matches a given request or response message.. /// internal static string NonNullUriRequiredForMediaTypeMapping { get { return ResourceManager.GetString("NonNullUriRequiredForMediaTypeMapping", resourceCulture); } } /// /// Looks up a localized string similar to No MediaTypeFormatter is available to read an object of type '{0}' from content with media type '{1}'.. /// internal static string NoReadSerializerAvailable { get { return ResourceManager.GetString("NoReadSerializerAvailable", resourceCulture); } } /// /// Looks up a localized string similar to Stream does not support reading.. /// internal static string NotSupported_UnreadableStream { get { return ResourceManager.GetString("NotSupported_UnreadableStream", resourceCulture); } } /// /// Looks up a localized string similar to Stream does not support seeking.. /// internal static string NotSupported_UnseekableStream { get { return ResourceManager.GetString("NotSupported_UnseekableStream", resourceCulture); } } /// /// Looks up a localized string similar to Stream does not support writing.. /// internal static string NotSupported_UnwritableStream { get { return ResourceManager.GetString("NotSupported_UnwritableStream", resourceCulture); } } /// /// Looks up a localized string similar to An object of type '{0}' cannot be used with a type parameter of '{1}'.. /// internal static string ObjectAndTypeDisagree { get { return ResourceManager.GetString("ObjectAndTypeDisagree", resourceCulture); } } /// /// Looks up a localized string similar to The configured formatter '{0}' cannot write an object of type '{1}'.. /// internal static string ObjectContent_FormatterCannotWriteType { get { return ResourceManager.GetString("ObjectContent_FormatterCannotWriteType", resourceCulture); } } /// /// Looks up a localized string similar to Cannot access a closed stream.. /// internal static string ObjectDisposed_StreamClosed { get { return ResourceManager.GetString("ObjectDisposed_StreamClosed", resourceCulture); } } /// /// Looks up a localized string similar to Query string name cannot be null.. /// internal static string QueryStringNameShouldNotNull { get { return ResourceManager.GetString("QueryStringNameShouldNotNull", resourceCulture); } } /// /// Looks up a localized string similar to Unexpected end of HTTP message stream. HTTP message is not complete.. /// internal static string ReadAsHttpMessageUnexpectedTermination { get { return ResourceManager.GetString("ReadAsHttpMessageUnexpectedTermination", resourceCulture); } } /// /// Looks up a localized string similar to Invalid '{0}' instance provided. It does not have a '{1}' content-type header with a '{2}' parameter.. /// internal static string ReadAsMimeMultipartArgumentNoBoundary { get { return ResourceManager.GetString("ReadAsMimeMultipartArgumentNoBoundary", resourceCulture); } } /// /// Looks up a localized string similar to Invalid '{0}' instance provided. It does not have a content-type header value. '{0}' instances must have a content-type header starting with '{1}'.. /// internal static string ReadAsMimeMultipartArgumentNoContentType { get { return ResourceManager.GetString("ReadAsMimeMultipartArgumentNoContentType", resourceCulture); } } /// /// Looks up a localized string similar to Invalid '{0}' instance provided. It does not have a content type header starting with '{1}'.. /// internal static string ReadAsMimeMultipartArgumentNoMultipart { get { return ResourceManager.GetString("ReadAsMimeMultipartArgumentNoMultipart", resourceCulture); } } /// /// Looks up a localized string similar to Error reading MIME multipart body part.. /// internal static string ReadAsMimeMultipartErrorReading { get { return ResourceManager.GetString("ReadAsMimeMultipartErrorReading", resourceCulture); } } /// /// Looks up a localized string similar to Error writing MIME multipart body part to output stream.. /// internal static string ReadAsMimeMultipartErrorWriting { get { return ResourceManager.GetString("ReadAsMimeMultipartErrorWriting", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing MIME multipart body part header byte {0} of data segment {1}.. /// internal static string ReadAsMimeMultipartHeaderParseError { get { return ResourceManager.GetString("ReadAsMimeMultipartHeaderParseError", resourceCulture); } } /// /// Looks up a localized string similar to Error parsing MIME multipart message byte {0} of data segment {1}.. /// internal static string ReadAsMimeMultipartParseError { get { return ResourceManager.GetString("ReadAsMimeMultipartParseError", resourceCulture); } } /// /// Looks up a localized string similar to The stream provider of type '{0}' threw an exception.. /// internal static string ReadAsMimeMultipartStreamProviderException { get { return ResourceManager.GetString("ReadAsMimeMultipartStreamProviderException", resourceCulture); } } /// /// Looks up a localized string similar to The stream provider of type '{0}' returned null. It must return a writable '{1}' instance.. /// internal static string ReadAsMimeMultipartStreamProviderNull { get { return ResourceManager.GetString("ReadAsMimeMultipartStreamProviderNull", resourceCulture); } } /// /// Looks up a localized string similar to The stream provider of type '{0}' returned a read-only stream. It must return a writable '{1}' instance.. /// internal static string ReadAsMimeMultipartStreamProviderReadOnly { get { return ResourceManager.GetString("ReadAsMimeMultipartStreamProviderReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to Unexpected end of MIME multipart stream. MIME multipart message is not complete.. /// internal static string ReadAsMimeMultipartUnexpectedTermination { get { return ResourceManager.GetString("ReadAsMimeMultipartUnexpectedTermination", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' method in '{1}' returned null. It must return a RemoteStreamResult instance containing a writable stream and a valid URL.. /// internal static string RemoteStreamInfoCannotBeNull { get { return ResourceManager.GetString("RemoteStreamInfoCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to The '{0}' serializer cannot serialize the type '{1}'.. /// internal static string SerializerCannotSerializeType { get { return ResourceManager.GetString("SerializerCannotSerializeType", resourceCulture); } } /// /// Looks up a localized string similar to There is an unmatched opened bracket for the '{0}' at position {1}.. /// internal static string UnMatchedBracketNotValid { get { return ResourceManager.GetString("UnMatchedBracketNotValid", resourceCulture); } } /// /// Looks up a localized string similar to Indentation is not supported by '{0}'.. /// internal static string UnsupportedIndent { get { return ResourceManager.GetString("UnsupportedIndent", resourceCulture); } } /// /// Looks up a localized string similar to Unable to validate types on this platform when {0} is 'false'. Please set {0} or move to a supported platform, one where the 'netstandard2.0' assembly is usable.. /// internal static string XmlMediaTypeFormatter_DCS_NotSupported { get { return ResourceManager.GetString("XmlMediaTypeFormatter_DCS_NotSupported", resourceCulture); } } /// /// Looks up a localized string similar to The object of type '{0}' returned by {1} must be an instance of either XmlObjectSerializer or XmlSerializer.. /// internal static string XmlMediaTypeFormatter_InvalidSerializerType { get { return ResourceManager.GetString("XmlMediaTypeFormatter_InvalidSerializerType", resourceCulture); } } /// /// Looks up a localized string similar to The object returned by {0} must not be a null value.. /// internal static string XmlMediaTypeFormatter_NullReturnedSerializer { get { return ResourceManager.GetString("XmlMediaTypeFormatter_NullReturnedSerializer", resourceCulture); } } } } ================================================ FILE: src/System.Net.Http.Formatting/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 A null '{0}' is not valid. The '{0}' of '{1}' cannot be used as a supported media type because it is a media range. The '{0}' type cannot accept a null value for the value type '{1}'. Error reading HTML form URL-encoded data stream. Error parsing HTML form URL-encoded data, byte {0}. Invalid HTTP status code: '{0}'. The status code must be between {1} and {2}. Invalid HTTP version: '{0}'. The version must start with the characters '{1}'. The '{0}' of the '{1}' has already been read. The '{0}' must be seekable in order to create an '{1}' instance containing an entity body. Error reading HTTP message. Invalid '{0}' instance provided. It does not have a content type header with a value of '{1}'. HTTP Request URI cannot be an empty string. Error parsing HTTP message header byte {0} of message {1}. An invalid number of '{0}' header fields were present in the HTTP Request. It must contain exactly one '{0}' header field but found {1}. Invalid URI scheme: '{0}'. The URI scheme must be a valid '{1}' scheme. MIME multipart boundary cannot end with an empty space. Did not find required '{0}' header field in MIME multipart body part. Could not determine a valid local file name for the multipart body part. A non-null request URI must be provided to determine if a '{0}' matches a given request or response message. No MediaTypeFormatter is available to read an object of type '{0}' from content with media type '{1}'. An object of type '{0}' cannot be used with a type parameter of '{1}'. Invalid '{0}' instance provided. It does not have a '{1}' content-type header with a '{2}' parameter. Invalid '{0}' instance provided. It does not have a content-type header value. '{0}' instances must have a content-type header starting with '{1}'. Invalid '{0}' instance provided. It does not have a content type header starting with '{1}'. Error reading MIME multipart body part. Error writing MIME multipart body part to output stream. Error parsing MIME multipart body part header byte {0} of data segment {1}. Error parsing MIME multipart message byte {0} of data segment {1}. The stream provider of type '{0}' threw an exception. The stream provider of type '{0}' returned null. It must return a writable '{1}' instance. The stream provider of type '{0}' returned a read-only stream. It must return a writable '{1}' instance. Unexpected end of MIME multipart stream. MIME multipart message is not complete. The '{0}' serializer cannot serialize the type '{1}'. Indentation is not supported by '{0}'. The media type formatter of type '{0}' does not support reading because it does not implement the ReadFromStreamAsync method. The media type formatter of type '{0}' does not support writing because it does not implement the WriteToStreamAsync method. The media type formatter of type '{0}' does not support reading because it does not implement the ReadFromStream method. The media type formatter of type '{0}' does not support writing because it does not implement the WriteToStream method. The configured formatter '{0}' cannot write an object of type '{1}'. Mismatched types at node '{0}'. Invalid array at node '{0}'. Traditional style array without '[]' is not supported with nested object at location {0}. The maximum read depth ({0}) has been exceeded because the form url-encoded data being read has more levels of nesting than is allowed. Nested bracket is not valid for '{0}' data at position {1}. Query string name cannot be null. There is an unmatched opened bracket for the '{0}' at position {1}. No encoding found for media type formatter '{0}'. There must be at least one supported encoding registered in order for the media type formatter to read or write content. The specified value is not a valid cookie name. Cookie cannot be null. The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class. Async Callback threw an exception. The IAsyncResult implementation '{0}' tried to complete a single operation multiple times. This could be caused by an incorrect application IAsyncResult implementation or other extensibility code, such as an IAsyncResult that returns incorrect CompletedSynchronously values or invokes the AsyncCallback multiple times. End cannot be called twice on an AsyncResult. An incorrect IAsyncResult was provided to an 'End' method. The IAsyncResult object passed to 'End' must be the one returned from the matching 'Begin' or passed to the callback provided to 'Begin'. The '{0}' list is invalid because it contains one or more null items. The '{0}' list is invalid because the property '{1}' of '{2}' is not null. Unexpected end of HTTP message stream. HTTP message is not complete. The range unit '{0}' is not valid. The range must have a unit of '{1}'. The 'From' value of the range must be less than or equal to {0}. None of the requested ranges ({0}) overlap with the current extent of the selected resource. The requested range ({0}) does not overlap with the current extent of the selected resource. The stream over which '{0}' provides a range view must be seekable. This is a read-only stream. Found zero byte ranges. There must be at least one byte range provided. The stream over which '{0}' provides a range view must have a length greater than or equal to 1. The object of type '{0}' returned by {1} must be an instance of either XmlObjectSerializer or XmlSerializer. The object returned by {0} must not be a null value. The '{0}' method returned null. It must return a JSON serializer instance. The '{0}' method threw an exception when attempting to create a JSON serializer. The '{0}' method returned null. It must return a JSON reader instance. The '{0}' method returned null. It must return a JSON writer instance. Error parsing BSON data; unable to read content as a {0}. Error parsing BSON data; unexpected dictionary content: {0} entries, first key '{1}'. The '{0}' method in '{1}' returned null. It must return a RemoteStreamResult instance containing a writable stream and a valid URL. An attempt was made to move the position before the beginning of the stream. Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. Stream does not support reading. Stream does not support seeking. Stream does not support writing. Cannot access a closed stream. Unable to validate types on this platform when {0} is 'true'. Please reset {0} or move to a supported platform, one where the .NET Standard 2.0 assembly is usable. Unable to validate types on this platform when {0} is 'false'. Please set {0} or move to a supported platform, one where the .NET Standard 2.0 assembly is usable. ================================================ FILE: src/System.Net.Http.Formatting/PushStreamContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Net.Http.Internal; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Provides an implementation that exposes an output /// which can be written to directly. The ability to push data to the output stream differs from the /// where data is pulled and not pushed. /// public class PushStreamContent : HttpContent { private readonly Func _onStreamAvailable; /// /// Initializes a new instance of the class. The /// action is called when an output stream /// has become available allowing the action to write to it directly. When the /// stream is closed, it will signal to the content that it has completed and the /// HTTP request or response will be completed. /// /// The action to call when an output stream is available. public PushStreamContent(Action onStreamAvailable) : this(Taskify(onStreamAvailable), (MediaTypeHeaderValue)null) { } /// /// Initializes a new instance of the class. /// /// The action to call when an output stream is available. When the /// output stream is closed or disposed, it will signal to the content that it has completed and the /// HTTP request or response will be completed. public PushStreamContent(Func onStreamAvailable) : this(onStreamAvailable, (MediaTypeHeaderValue)null) { } /// /// Initializes a new instance of the class with the given media type. /// /// The action to call when an output stream is available. /// The value of the Content-Type content header on an HTTP response. public PushStreamContent(Action onStreamAvailable, string mediaType) : this(Taskify(onStreamAvailable), new MediaTypeHeaderValue(mediaType)) { } /// /// Initializes a new instance of the class with the given media type. /// /// The action to call when an output stream is available. When the /// output stream is closed or disposed, it will signal to the content that it has completed and the /// HTTP request or response will be completed. /// The value of the Content-Type content header on an HTTP response. public PushStreamContent(Func onStreamAvailable, string mediaType) : this(onStreamAvailable, new MediaTypeHeaderValue(mediaType)) { } /// /// Initializes a new instance of the class with the given . /// /// The action to call when an output stream is available. /// The value of the Content-Type content header on an HTTP response. public PushStreamContent(Action onStreamAvailable, MediaTypeHeaderValue mediaType) : this(Taskify(onStreamAvailable), mediaType) { } /// /// Initializes a new instance of the class with the given . /// /// The action to call when an output stream is available. When the /// output stream is closed or disposed, it will signal to the content that it has completed and the /// HTTP request or response will be completed. /// The value of the Content-Type content header on an HTTP response. public PushStreamContent(Func onStreamAvailable, MediaTypeHeaderValue mediaType) { if (onStreamAvailable == null) { throw Error.ArgumentNull("onStreamAvailable"); } _onStreamAvailable = onStreamAvailable; Headers.ContentType = mediaType ?? MediaTypeConstants.ApplicationOctetStreamMediaType; } private static Func Taskify( Action onStreamAvailable) { if (onStreamAvailable == null) { throw Error.ArgumentNull("onStreamAvailable"); } return (Stream stream, HttpContent content, TransportContext transportContext) => { onStreamAvailable(stream, content, transportContext); return TaskHelpers.Completed(); }; } /// /// When this method is called, it calls the action provided in the constructor with the output /// stream to write to. Once the action has completed its work it closes the stream which will /// close this content instance and complete the HTTP request or response. /// /// The to which to write. /// The associated . /// A instance that is asynchronously serializing the object's content. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is passed as task result.")] protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) { TaskCompletionSource serializeToStreamTask = new TaskCompletionSource(); Stream wrappedStream = new CompleteTaskOnCloseStream(stream, serializeToStreamTask); await _onStreamAvailable(wrappedStream, this, context); // wait for wrappedStream.Close/Dispose to get called. await serializeToStreamTask.Task; } /// /// Computes the length of the stream if possible. /// /// The computed length of the stream. /// true if the length has been computed; otherwise false. protected override bool TryComputeLength(out long length) { // We can't know the length of the content being pushed to the output stream. length = -1; return false; } internal class CompleteTaskOnCloseStream : DelegatingStream { private TaskCompletionSource _serializeToStreamTask; public CompleteTaskOnCloseStream(Stream innerStream, TaskCompletionSource serializeToStreamTask) : base(innerStream) { Contract.Assert(serializeToStreamTask != null); _serializeToStreamTask = serializeToStreamTask; } #if NETSTANDARD1_3 [SuppressMessage( "Microsoft.Usage", "CA2215:Dispose methods should call base class dispose", Justification = "See comments, this is intentional.")] protected override void Dispose(bool disposing) { // We don't dispose the underlying stream because we don't own it. Dispose in this case just signifies // that the user's action is finished. _serializeToStreamTask.TrySetResult(true); } #else public override void Close() { // We don't Close the underlying stream because we don't own it. Dispose in this case just signifies // that the user's action is finished. _serializeToStreamTask.TrySetResult(true); } #endif } } } ================================================ FILE: src/System.Net.Http.Formatting/RemoteStreamInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Web.Http; namespace System.Net.Http { /// /// Represents the result for . /// public class RemoteStreamInfo { /// /// Initializes a new instance of the class. /// /// /// The remote stream instance where the file will be written to. /// /// The remote file's location. /// The remote file's name. public RemoteStreamInfo(Stream remoteStream, string location, string fileName) { if (remoteStream == null) { throw Error.ArgumentNull("remoteStream"); } if (location == null) { throw Error.ArgumentNull("location"); } if (fileName == null) { throw Error.ArgumentNull("fileName"); } FileName = fileName; RemoteStream = remoteStream; Location = location; } /// /// Gets the remote file's location. /// public string FileName { get; private set; } /// /// Gets the remote file's location. /// public string Location { get; private set; } /// /// Gets the remote stream instance where the file will be written to. /// public Stream RemoteStream { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj ================================================  {668E9021-CE84-49D9-98FB-DF125A9FCDB0} Library Properties System.Net.Http System.Net.Http.Formatting $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETHTTPFORMATTING Client 1591 ..\..\packages\System.Buffers.4.5.1\lib\netstandard2.0\System.Buffers.dll False False ..\..\packages\System.Memory.4.5.5\lib\netstandard2.0\System.Memory.dll False False ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll False False ..\..\packages\NETStandard.Library.2.0.3\lib\netstandard2.0\netstandard.dll False False ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll False False ..\..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll False False True CommonWebApiResources.resx True Properties\CommonWebApiResources.Designer.cs ResXFileCodeGenerator CommonWebApiResources.Designer.cs Properties\CommonWebApiResources.resx True Resources.resx True ResXFileCodeGenerator Resources.Designer.cs Designer ================================================ FILE: src/System.Net.Http.Formatting/UnsupportedMediaTypeException.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Web.Http; namespace System.Net.Http { /// /// Defines an exception type for signalling that a request's media type was not supported. /// [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "This type is not meant to be serialized")] [SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly", Justification = "This type is not meant to be serialized")] [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "UnsupportedMediaTypeException is only used to propagate the media type back to the server layer")] public class UnsupportedMediaTypeException : Exception { /// /// Initializes a new instance of the class. /// /// The message that describes the error. /// The unsupported media type. public UnsupportedMediaTypeException(string message, MediaTypeHeaderValue mediaType) : base(message) { if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } MediaType = mediaType; } public MediaTypeHeaderValue MediaType { get; private set; } } } ================================================ FILE: src/System.Net.Http.Formatting/UriExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Formatting; using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace System.Net.Http { /// /// Extension methods to allow strongly typed objects to be read from the query component of instances. /// [EditorBrowsable(EditorBrowsableState.Never)] public static class UriExtensions { /// /// Parses the query portion of the specified . /// /// The instance from which to read. /// A containing the parsed result. public static NameValueCollection ParseQueryString(this Uri address) { if (address == null) { throw Error.ArgumentNull("address"); } return new FormDataCollection(address).ReadAsNameValueCollection(); } /// /// Reads HTML form URL encoded data provided in the query component as a object. /// /// The instance from which to read. /// An object to be initialized with this instance or null if the conversion cannot be performed. /// true if the query component can be read as ; otherwise false. public static bool TryReadQueryAsJson(this Uri address, out JObject value) { if (address == null) { throw Error.ArgumentNull("address"); } IEnumerable> query = new FormDataCollection(address); return FormUrlEncodedJson.TryParse(query, out value); } /// /// Reads HTML form URL encoded data provided in the query component as an of the given . /// /// The instance from which to read. /// The type of the object to read. /// An object to be initialized with this instance or null if the conversion cannot be performed. /// true if the query component can be read as the specified type; otherwise false. [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "This is the non-generic version.")] public static bool TryReadQueryAs(this Uri address, Type type, out object value) { if (address == null) { throw Error.ArgumentNull("address"); } if (type == null) { throw Error.ArgumentNull("type"); } IEnumerable> query = new FormDataCollection(address); JObject jsonObject; if (FormUrlEncodedJson.TryParse(query, out jsonObject)) { using (JTokenReader jsonReader = new JTokenReader(jsonObject)) { value = new JsonSerializer().Deserialize(jsonReader, type); } return true; } value = null; return false; } /// /// Reads HTML form URL encoded data provided in the query component as an of type . /// /// The type of the object to read. /// The instance from which to read. /// An object to be initialized with this instance or null if the conversion cannot be performed. /// true if the query component can be read as the specified type; otherwise false. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "The T represents the output parameter, not an input parameter.")] public static bool TryReadQueryAs(this Uri address, out T value) { if (address == null) { throw Error.ArgumentNull("address"); } IEnumerable> query = new FormDataCollection(address); JObject jsonObject; if (FormUrlEncodedJson.TryParse(query, out jsonObject)) { value = jsonObject.ToObject(); return true; } value = default(T); return false; } } } ================================================ FILE: src/System.Net.Http.Formatting/packages.config ================================================  ================================================ FILE: src/System.Net.Http.Formatting.ns1_3/ICloneable.cs ================================================ // No ICloneable interface in .NET Standard 1.3. namespace System { internal interface ICloneable { object Clone(); } } ================================================ FILE: src/System.Net.Http.Formatting.ns1_3/MediaTypeHeaderValueExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; using System.Net.Http.Headers; namespace System.Net.Http { internal static class MediaTypeHeaderValueExtensions { public static MediaTypeHeaderValue Clone(this MediaTypeHeaderValue mediaType) { Contract.Assert(mediaType != null && mediaType.GetType() == typeof(MediaTypeHeaderValue)); var result = new MediaTypeHeaderValue(mediaType.MediaType); foreach (var parameter in mediaType.Parameters) { result.Parameters.Add(new NameValueHeaderValue(parameter.Name, parameter.Value)); } return result; } } } ================================================ FILE: src/System.Net.Http.Formatting.ns1_3/System.Net.Http.Formatting.ns1_3.csproj ================================================  netstandard1.3 System.Net.Http System.Net.Http.Formatting $(OutputPath)ns1_3\ $(OutputPath)$(AssemblyName).xml false $(DefineConstants);ASPNETHTTPFORMATTING 1591 false $(Configurations);CodeAnalysis false %(RecursiveDir)\%(Filename).cs True CommonWebApiResources.resx True Properties\CommonWebApiResources.Designer.cs ResXFileCodeGenerator CommonWebApiResources.Designer.cs Properties\CommonWebApiResources.resx True Resources.resx True Properties\Resources.Designer.cs ResXFileCodeGenerator Resources.Designer.cs Properties\Resources.resx Designer ================================================ FILE: src/System.Net.Http.Formatting.ns2_0/System.Net.Http.Formatting.ns2_0.csproj ================================================  netstandard2.0 System.Net.Http System.Net.Http.Formatting $(OutputPath)ns2_0\ $(OutputPath)$(AssemblyName).xml false $(DefineConstants);ASPNETHTTPFORMATTING 1591 false $(Configurations);CodeAnalysis false %(RecursiveDir)\%(Filename).cs True CommonWebApiResources.resx True Properties\CommonWebApiResources.Designer.cs ResXFileCodeGenerator CommonWebApiResources.Designer.cs Properties\CommonWebApiResources.resx True Resources.resx True Properties\Resources.Designer.cs ResXFileCodeGenerator Resources.Designer.cs Properties\Resources.resx Designer ================================================ FILE: src/System.Web.Cors/CorsConstants.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Cors { /// /// CORS-related constants. /// public static class CorsConstants { /// /// The HTTP method for the CORS preflight request. /// public static readonly string PreflightHttpMethod = "OPTIONS"; /// /// The Origin request header. /// public static readonly string Origin = "Origin"; /// /// The value for the Access-Control-Allow-Origin response header to allow all origins. /// public static readonly string AnyOrigin = "*"; /// /// The Access-Control-Request-Method request header. /// public static readonly string AccessControlRequestMethod = "Access-Control-Request-Method"; /// /// The Access-Control-Request-Headers request header. /// public static readonly string AccessControlRequestHeaders = "Access-Control-Request-Headers"; /// /// The Access-Control-Allow-Origin response header. /// public static readonly string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; /// /// The Access-Control-Allow-Headers response header. /// public static readonly string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; /// /// The Access-Control-Expose-Headers response header. /// public static readonly string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; /// /// The Access-Control-Allow-Methods response header. /// public static readonly string AccessControlAllowMethods = "Access-Control-Allow-Methods"; /// /// The Access-Control-Allow-Credentials response header. /// public static readonly string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; /// /// The Access-Control-Max-Age response header. /// public static readonly string AccessControlMaxAge = "Access-Control-Max-Age"; internal static readonly string[] SimpleRequestHeaders = { "Origin", "Accept", "Accept-Language", "Content-Language", }; internal static readonly string[] SimpleResponseHeaders = { "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma" }; internal static readonly string[] SimpleMethods = { "GET", "HEAD", "POST" }; } } ================================================ FILE: src/System.Web.Cors/CorsEngine.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Web.Cors.Properties; namespace System.Web.Cors { /// /// An implementation of based on the CORS specifications. /// public class CorsEngine : ICorsEngine { /// /// Evaluates the policy. /// /// The . /// The . /// /// The /// /// /// requestContext /// or /// policy /// public virtual CorsResult EvaluatePolicy(CorsRequestContext requestContext, CorsPolicy policy) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (policy == null) { throw new ArgumentNullException("policy"); } CorsResult result = new CorsResult(); if (!TryValidateOrigin(requestContext, policy, result)) { return result; } result.SupportsCredentials = policy.SupportsCredentials; if (requestContext.IsPreflight) { if (!TryValidateMethod(requestContext, policy, result)) { return result; } if (!TryValidateHeaders(requestContext, policy, result)) { return result; } result.PreflightMaxAge = policy.PreflightMaxAge; } else { AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders); } return result; } /// /// Try to validate the requested method based on . /// /// The . /// The . /// The . /// true if the requested method is valid; otherwise, false. /// /// requestContext /// or /// policy /// or /// result /// public virtual bool TryValidateMethod(CorsRequestContext requestContext, CorsPolicy policy, CorsResult result) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (policy == null) { throw new ArgumentNullException("policy"); } if (result == null) { throw new ArgumentNullException("result"); } if (policy.AllowAnyMethod || policy.Methods.Contains(requestContext.AccessControlRequestMethod)) { result.AllowedMethods.Add(requestContext.AccessControlRequestMethod); } else { result.ErrorMessages.Add(String.Format( CultureInfo.CurrentCulture, SRResources.MethodNotAllowed, requestContext.AccessControlRequestMethod)); } return result.IsValid; } /// /// Try to validate the requested headers based on . /// /// The . /// The . /// The . /// true if the requested headers are valid; otherwise, false. /// /// requestContext /// or /// policy /// or /// result /// public virtual bool TryValidateHeaders(CorsRequestContext requestContext, CorsPolicy policy, CorsResult result) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (policy == null) { throw new ArgumentNullException("policy"); } if (result == null) { throw new ArgumentNullException("result"); } if (policy.AllowAnyHeader || requestContext.AccessControlRequestHeaders.IsSubsetOf(policy.Headers)) { AddHeaderValues(result.AllowedHeaders, requestContext.AccessControlRequestHeaders); } else { result.ErrorMessages.Add(String.Format( CultureInfo.CurrentCulture, SRResources.HeadersNotAllowed, String.Join(",", requestContext.AccessControlRequestHeaders))); } return result.IsValid; } /// /// Try to validate the request origin based on . /// /// The . /// The . /// The . /// true if the request origin is valid; otherwise, false. /// /// requestContext /// or /// policy /// or /// result /// public virtual bool TryValidateOrigin(CorsRequestContext requestContext, CorsPolicy policy, CorsResult result) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (policy == null) { throw new ArgumentNullException("policy"); } if (result == null) { throw new ArgumentNullException("result"); } if (requestContext.Origin != null) { if (policy.AllowAnyOrigin) { if (policy.SupportsCredentials) { result.AllowedOrigin = requestContext.Origin; } else { result.AllowedOrigin = CorsConstants.AnyOrigin; } } else if (policy.Origins.Contains(requestContext.Origin)) { result.AllowedOrigin = requestContext.Origin; } else { result.ErrorMessages.Add(String.Format( CultureInfo.CurrentCulture, SRResources.OriginNotAllowed, requestContext.Origin)); } } else { result.ErrorMessages.Add(SRResources.NoOriginHeader); } return result.IsValid; } private static void AddHeaderValues(IList target, IEnumerable headerValues) { foreach (string headerValue in headerValues) { target.Add(headerValue); } } } } ================================================ FILE: src/System.Web.Cors/CorsPolicy.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Text; using System.Web.Cors.Properties; namespace System.Web.Cors { /// /// Defines the policy for Cross-Origin requests based on the CORS specifications. /// public class CorsPolicy { private long? _preflightMaxAge; /// /// Initializes a new instance of the class. /// public CorsPolicy() { ExposedHeaders = new List(); Headers = new List(); Methods = new List(); Origins = new List(); } /// /// Gets or sets a value indicating whether to allow all headers. /// public bool AllowAnyHeader { get; set; } /// /// Gets or sets a value indicating whether to allow all methods. /// public bool AllowAnyMethod { get; set; } /// /// Gets or sets a value indicating whether to allow all origins. /// public bool AllowAnyOrigin { get; set; } /// /// Gets the headers that the resource might use and can be exposed. /// public IList ExposedHeaders { get; private set; } /// /// Gets the headers that are supported by the resource. /// public IList Headers { get; private set; } /// /// Gets the methods that are supported by the resource. /// public IList Methods { get; private set; } /// /// Gets the origins that are allowed to access the resource. /// public IList Origins { get; private set; } /// /// Gets or sets the number of seconds the results of a preflight request can be cached. /// public long? PreflightMaxAge { get { return _preflightMaxAge; } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SRResources.PreflightMaxAgeOutOfRange); } _preflightMaxAge = value; } } /// /// Gets or sets a value indicating whether the resource supports user credentials in the request. /// public bool SupportsCredentials { get; set; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("AllowAnyHeader: "); builder.Append(AllowAnyHeader); builder.Append(", AllowAnyMethod: "); builder.Append(AllowAnyMethod); builder.Append(", AllowAnyOrigin: "); builder.Append(AllowAnyOrigin); builder.Append(", PreflightMaxAge: "); builder.Append(PreflightMaxAge.HasValue ? PreflightMaxAge.Value.ToString(CultureInfo.InvariantCulture) : "null"); builder.Append(", SupportsCredentials: "); builder.Append(SupportsCredentials); builder.Append(", Origins: {"); builder.Append(String.Join(",", Origins)); builder.Append("}"); builder.Append(", Methods: {"); builder.Append(String.Join(",", Methods)); builder.Append("}"); builder.Append(", Headers: {"); builder.Append(String.Join(",", Headers)); builder.Append("}"); builder.Append(", ExposedHeaders: {"); builder.Append(String.Join(",", ExposedHeaders)); builder.Append("}"); return builder.ToString(); } } } ================================================ FILE: src/System.Web.Cors/CorsRequestContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Text; namespace System.Web.Cors { /// /// Provides access to CORS-specific information on the request. /// public class CorsRequestContext { /// /// Initializes a new instance of the class. /// public CorsRequestContext() { AccessControlRequestHeaders = new HashSet(StringComparer.OrdinalIgnoreCase); Properties = new Dictionary(); } /// /// Gets or sets the request URI. /// public Uri RequestUri { get; set; } /// /// Gets or sets the request method. /// public string HttpMethod { get; set; } /// /// Gets or sets the Origin header value. /// public string Origin { get; set; } /// /// Gets or sets the Host header value. /// public string Host { get; set; } /// /// Gets or sets the Access-Control-Request-Method header value. /// public string AccessControlRequestMethod { get; set; } /// /// Gets the Access-Control-Request-Headers header value. /// public ISet AccessControlRequestHeaders { get; private set; } /// /// Gets a set of properties for the . /// public IDictionary Properties { get; private set; } /// /// Gets a value indicating whether this is a preflight request. /// public bool IsPreflight { get { return Origin != null && AccessControlRequestMethod != null && String.Equals(HttpMethod, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase); } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("Origin: "); builder.Append(Origin ?? "null"); builder.Append(", HttpMethod: "); builder.Append(HttpMethod ?? "null"); builder.Append(", IsPreflight: "); builder.Append(IsPreflight); builder.Append(", Host: "); builder.Append(Host); builder.Append(", AccessControlRequestMethod: "); builder.Append(AccessControlRequestMethod ?? "null"); builder.Append(", RequestUri: "); builder.Append(RequestUri); builder.Append(", AccessControlRequestHeaders: {"); builder.Append(String.Join(",", AccessControlRequestHeaders)); builder.Append("}"); return builder.ToString(); } } } ================================================ FILE: src/System.Web.Cors/CorsResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Web.Cors.Properties; namespace System.Web.Cors { /// /// Results returned by . /// public class CorsResult { private long? _preflightMaxAge; /// /// Initializes a new instance of the class. /// public CorsResult() { AllowedMethods = new List(); AllowedHeaders = new List(); AllowedExposedHeaders = new List(); ErrorMessages = new List(); } /// /// Gets a value indicating whether the result is valid. /// public bool IsValid { get { return ErrorMessages.Count == 0; } } /// /// Gets the error messages. /// public IList ErrorMessages { get; private set; } /// /// Gets or sets the allowed origin. /// public string AllowedOrigin { get; set; } /// /// Gets or sets a value indicating whether the resource supports user credentials. /// public bool SupportsCredentials { get; set; } /// /// Gets the allowed methods. /// public IList AllowedMethods { get; private set; } /// /// Gets the allowed headers. /// public IList AllowedHeaders { get; private set; } /// /// Gets the allowed headers that can be exposed on the response. /// public IList AllowedExposedHeaders { get; private set; } /// /// Gets or sets the number of seconds the results of a preflight request can be cached. /// public long? PreflightMaxAge { get { return _preflightMaxAge; } set { if (value < 0) { throw new ArgumentOutOfRangeException("value", SRResources.PreflightMaxAgeOutOfRange); } _preflightMaxAge = value; } } /// /// Returns CORS-specific headers that should be added to the response. /// /// The response headers. public virtual IDictionary ToResponseHeaders() { IDictionary headers = new Dictionary(); if (AllowedOrigin != null) { headers.Add(CorsConstants.AccessControlAllowOrigin, AllowedOrigin); } if (SupportsCredentials) { headers.Add(CorsConstants.AccessControlAllowCredentials, "true"); } if (AllowedMethods.Count > 0) { // Filter out simple methods IEnumerable nonSimpleAllowMethods = AllowedMethods.Where(m => !CorsConstants.SimpleMethods.Contains(m, StringComparer.OrdinalIgnoreCase)); AddHeader(headers, CorsConstants.AccessControlAllowMethods, nonSimpleAllowMethods); } if (AllowedHeaders.Count > 0) { // Filter out simple request headers IEnumerable nonSimpleAllowRequestHeaders = AllowedHeaders.Where(header => !CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase)); AddHeader(headers, CorsConstants.AccessControlAllowHeaders, nonSimpleAllowRequestHeaders); } if (AllowedExposedHeaders.Count > 0) { // Filter out simple response headers IEnumerable nonSimpleAllowResponseHeaders = AllowedExposedHeaders.Where(header => !CorsConstants.SimpleResponseHeaders.Contains(header, StringComparer.OrdinalIgnoreCase)); AddHeader(headers, CorsConstants.AccessControlExposeHeaders, nonSimpleAllowResponseHeaders); } if (PreflightMaxAge.HasValue) { headers.Add(CorsConstants.AccessControlMaxAge, PreflightMaxAge.ToString()); } return headers; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("IsValid: "); builder.Append(IsValid); builder.Append(", AllowCredentials: "); builder.Append(SupportsCredentials); builder.Append(", PreflightMaxAge: "); builder.Append(PreflightMaxAge.HasValue ? PreflightMaxAge.Value.ToString(CultureInfo.InvariantCulture) : "null"); builder.Append(", AllowOrigin: "); builder.Append(AllowedOrigin); builder.Append(", AllowExposedHeaders: {"); builder.Append(String.Join(",", AllowedExposedHeaders)); builder.Append("}"); builder.Append(", AllowHeaders: {"); builder.Append(String.Join(",", AllowedHeaders)); builder.Append("}"); builder.Append(", AllowMethods: {"); builder.Append(String.Join(",", AllowedMethods)); builder.Append("}"); builder.Append(", ErrorMessages: {"); builder.Append(String.Join(",", ErrorMessages)); builder.Append("}"); return builder.ToString(); } private static void AddHeader(IDictionary headers, string headerName, IEnumerable headerValues) { string methods = String.Join(",", headerValues); if (!String.IsNullOrEmpty(methods)) { headers.Add(headerName, methods); } } } } ================================================ FILE: src/System.Web.Cors/ICorsEngine.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Cors { /// /// Provides an abstraction for evaluating CORS requests based on . /// public interface ICorsEngine { /// /// Evaluates the policy. /// /// The . /// The . /// The CorsResult EvaluatePolicy(CorsRequestContext requestContext, CorsPolicy policy); } } ================================================ FILE: src/System.Web.Cors/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Reflection; [assembly: AssemblyTitle("System.Web.Cors")] [assembly: AssemblyDescription("")] [assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "This assembly is delay-signed.")] ================================================ FILE: src/System.Web.Cors/Properties/SRResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.18033 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.Cors.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class SRResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal SRResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.Cors.Properties.SRResources", typeof(SRResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to The collection of headers '{0}' is not allowed.. /// internal static string HeadersNotAllowed { get { return ResourceManager.GetString("HeadersNotAllowed", resourceCulture); } } /// /// Looks up a localized string similar to The method '{0}' is not allowed.. /// internal static string MethodNotAllowed { get { return ResourceManager.GetString("MethodNotAllowed", resourceCulture); } } /// /// Looks up a localized string similar to The request does not contain the Origin header.. /// internal static string NoOriginHeader { get { return ResourceManager.GetString("NoOriginHeader", resourceCulture); } } /// /// Looks up a localized string similar to The origin '{0}' is not allowed.. /// internal static string OriginNotAllowed { get { return ResourceManager.GetString("OriginNotAllowed", resourceCulture); } } /// /// Looks up a localized string similar to PreflightMaxAge must be greater than or equal to 0.. /// internal static string PreflightMaxAgeOutOfRange { get { return ResourceManager.GetString("PreflightMaxAgeOutOfRange", resourceCulture); } } } } ================================================ FILE: src/System.Web.Cors/Properties/SRResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 The collection of headers '{0}' is not allowed. The method '{0}' is not allowed. The request does not contain the Origin header. The origin '{0}' is not allowed. PreflightMaxAge must be greater than or equal to 0. ================================================ FILE: src/System.Web.Cors/System.Web.Cors.csproj ================================================  {43C1B979-D593-4A32-BB3A-4316F1C66D66} Library Properties System.Web.Cors System.Web.Cors $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETMVC true Properties\CommonAssemblyInfo.cs SRResources.resx True True ResXFileCodeGenerator SRResources.Designer.cs ================================================ FILE: src/System.Web.Helpers/Chart/Chart.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Text; using System.Web.Helpers.Resources; using System.Web.Hosting; using System.Web.UI.DataVisualization.Charting; using System.Web.UI.WebControls; using System.Web.WebPages; using System.Xml; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { // Post-Beta Work: // -DataBind and Points.DataBind - need to find scenarios // -Elements: Annotations, MapAreas // -Interactivity / AJAX support? public class Chart { private readonly int _height; private readonly int _width; private readonly string _themePath; private readonly string _theme; private readonly List _legends = new List(); private readonly List _series = new List(); private readonly List _titles = new List(); private HttpContextBase _httpContext; private Func _virtualPathProviderFunc; private string _path; private DataSourceData _dataSource; [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "_xAxis, _yAxis", Justification = "These names make most sense.")] private ChartAxisData _xAxis, _yAxis; #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif /// Chart width in pixels. /// Chart height in pixels. /// String containing chart theme definition. Chart's theme defines properties like colors, positions, etc. /// This parameter is primarily meant for one of the predefined Chart themes, however any valid chart theme is acceptable. /// Path to a file containing definition of chart theme, default is none. /// Both the theme and themePath parameters can be specified. In this case, the Chart class applies the theme xml first /// followed by the content of file at themePath. /// /// /// Chart(100, 100, theme: ChartTheme.Blue) /// Chart(100, 100, theme: ChartTheme.Vanilla, themePath: "my-theme.xml") /// Chart(100, 100, theme: ".... definition inline ...." ) /// Chart(100, 100, themePath: "my-theme.xml") /// Any valid theme definition can be used as content of the file specified in themePath /// public Chart( int width, int height, string theme = null, string themePath = null) : this(GetDefaultContext(), () => HostingEnvironment.VirtualPathProvider, width, height, theme, themePath) { } // Overload used only for testing internal Chart(HttpContextBase httpContext, VirtualPathProvider virtualPathProvider, int width, int height, string theme = null, string themePath = null) : this(httpContext, () => virtualPathProvider, width, height, theme, themePath) { } internal Chart(HttpContextBase httpContext, Func virtualPathProviderFunc, int width, int height, string theme = null, string themePath = null) { Contract.Assert(httpContext != null); Contract.Assert(virtualPathProviderFunc != null); // HostingEnvironment.VirtualPathProvider never null in running host but may change at any time. Contract.Assert(virtualPathProviderFunc() != null); if (width < 0) { throw new ArgumentOutOfRangeException("width", String.Format( CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 0)); } if (height < 0) { throw new ArgumentOutOfRangeException("height", String.Format( CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 0)); } _httpContext = httpContext; _virtualPathProviderFunc = virtualPathProviderFunc; _width = width; _height = height; _theme = theme; // path must be app-relative in case chart is rendered from handler in different directory if (!String.IsNullOrEmpty(themePath)) { _themePath = VirtualPathUtil.ResolvePath(TemplateStack.GetCurrentTemplate(httpContext), httpContext, themePath); if (!virtualPathProviderFunc().FileExists(_themePath)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.Chart_ThemeFileNotFound, _themePath), "themePath"); } } } public string FileName { get { return _path; } } public int Height { get { return _height; } } public int Width { get { return _width; } } /// Legend title. /// Legend name. public Chart AddLegend( string title = null, string name = null) { _legends.Add(new LegendData { Name = name, Title = title }); return this; } /// Series name. /// Chart type (see: SeriesChartType). /// Chart area where the series is displayed. /// Axis label for the series. /// Legend for the series. /// Axis marker step. /// X data source, if data-binding the series. /// Column for the X data points, if data-binding the series. /// Y data source(s), if data-binding the series. /// Column(s) for the Y data points, if data-binding the series. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x", Justification = "Name based on X-axis. Suppressed in source because this is a one-time occurrence")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y", Justification = "Name based on Y-axis. Suppressed in source because this is a one-time occurrence")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "xValue, xField, yValues, yFields", Justification = "These names cannot be changed because this is a public method.")] public Chart AddSeries( string name = null, string chartType = "Column", string chartArea = null, string axisLabel = null, string legend = null, int markerStep = 1, IEnumerable xValue = null, string xField = null, IEnumerable yValues = null, string yFields = null) { if (String.IsNullOrEmpty(chartType)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "chartType"); } DataSourceData dataSource = null; if (yValues != null) { dataSource = new DataSourceData { XDataSource = xValue, XField = xField, DataSource = yValues, YFields = yFields }; } _series.Add(new SeriesData { Name = name, ChartType = ConvertStringArgument("chartType", chartType), ChartArea = chartArea, AxisLabel = axisLabel, Legend = legend, MarkerStep = markerStep, DataSource = dataSource }); return this; } /// Title text. /// Title name. public Chart AddTitle( string text = null, string name = null) { _titles.Add(new TitleData { Name = name, Text = text }); return this; } /// Title for X-axis /// The minimum value on X-axis. Default 0 /// The maximum value on X-axis. Default NaN public Chart SetXAxis( string title = "", double min = 0, double max = Double.NaN) { _xAxis = new ChartAxisData { Title = title, Minimum = min, Maximum = max }; return this; } /// Title for Y-axis /// The minimum value on Y-axis. Default 0 /// The maximum value on Y-axis. Default NaN public Chart SetYAxis( string title = "", double min = 0, double max = Double.NaN) { _yAxis = new ChartAxisData { Title = title, Minimum = min, Maximum = max }; return this; } /// /// Data-binds the chart by grouping values in a series. The series will be created by the chart. /// /// Chart data source. /// Column which series should be grouped by. /// Column for the X data points. /// Column(s) for the Y data points, separated by comma. /// /// Sort order (see: PointSortOrder). [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x", Justification = "Name based on X-axis. Suppressed in source because this is a one-time occurrence")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y", Justification = "Name based on Y-axis. Suppressed in source because this is a one-time occurrence")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "xField, yFields", Justification = "These names cannot be changed because this is a public method.")] public Chart DataBindCrossTable(IEnumerable dataSource, string groupByField, string xField, string yFields, string otherFields = null, string pointSortOrder = "Ascending") { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (dataSource is string) { throw new ArgumentException(HelpersResources.Chart_ExceptionDataBindSeriesToString, "dataSource"); } if (String.IsNullOrEmpty(groupByField)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "groupByField"); } if (String.IsNullOrEmpty(yFields)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "yFields"); } _dataSource = new DataSourceData { DataSource = dataSource, GroupByField = groupByField, XField = xField, YFields = yFields, OtherFields = otherFields, PointSortOrder = ConvertStringArgument("pointSortOrder", pointSortOrder) }; return this; } /// /// Data-binds the chart using a data source, with multiple y values supported. The series will be created by the chart. /// /// Chart data source. /// Column for the X data points. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x", Justification = "Name based on X-axis. Suppressed in source because this is a one-time occurrence")] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "xField", Justification = "These names cannot be changed because this is a public method.")] public Chart DataBindTable(IEnumerable dataSource, string xField = null) { if (dataSource == null) { throw new ArgumentNullException("dataSource"); } if (dataSource is string) { throw new ArgumentException(HelpersResources.Chart_ExceptionDataBindSeriesToString, "dataSource"); } _dataSource = new DataSourceData { DataBindTable = true, DataSource = dataSource, XField = xField }; return this; } /// /// Get the bytes for the chart image. /// /// Image format (see: ChartImageFormat). public byte[] GetBytes(string format = "jpeg") { var imageFormat = ConvertStringToChartImageFormat(format); using (MemoryStream stream = new MemoryStream()) { ExecuteChartAction(c => { c.SaveImage(stream, imageFormat); }); return stream.ToArray(); } } #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif /// /// Loads a chart from the cache. This can be used to render from an image handler. /// /// Cache key. public static Chart GetFromCache(string key) { return GetFromCache(GetDefaultContext(), key); } /// /// Saves the chart image to a file. /// /// File path. /// Chart image format (see: ChartImageFormat). public Chart Save(string path, string format = "jpeg") { return Save(GetDefaultContext(), path, format); } internal Chart Save(HttpContextBase httpContext, string path, string format) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } var imageFormat = ConvertStringToChartImageFormat(format); _path = VirtualPathUtil.MapPath(httpContext, path); ExecuteChartAction(c => { c.RenderType = RenderType.ImageTag; c.SaveImage(FileName, imageFormat); }); return this; } /// /// Saves the chart in cache. This can be used to render from an image handler. /// /// Cache key. Uses new GUID by default. /// Number of minutes to save in cache. /// Whether a sliding expiration policy is used. /// Cache key. public string SaveToCache(string key = null, int minutesToCache = 20, bool slidingExpiration = true) { if (String.IsNullOrEmpty(key)) { key = GetUniqueKey(); } WebCache.Set(key, this, minutesToCache, slidingExpiration); return key; } /// /// Saves the chart to the specified template file. /// /// XML template file path. public Chart SaveXml(string path) { return SaveXml(GetDefaultContext(), path); } /// /// Saves the chart to the specified template file. /// /// The . /// XML template file path. internal Chart SaveXml(HttpContextBase httpContext, string path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } ExecuteChartAction(c => { c.SaveXml(VirtualPathUtil.MapPath(httpContext, path)); }); return this; } public WebImage ToWebImage(string format = "jpeg") { return new WebImage(GetBytes(format)); } /// /// Writes the chart image to the response stream. This can be used to render from an image handler. /// /// Image format (see: ChartImageFormat). public Chart Write(string format = "jpeg") { var response = _httpContext.Response; response.Charset = String.Empty; response.ContentType = "image/" + NormalizeFormat(format); response.BinaryWrite(GetBytes(format)); return this; } #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif /// /// Writes a chart stored in cache to the response stream. This can be used to render from an image handler. /// /// Cache key. /// Image format (see: ChartImageFormat). public static Chart WriteFromCache(string key, string format = "jpeg") { return WriteFromCache(GetDefaultContext(), key, format); } // create and execute an action against the WebForm control in a limited scope since the control is disposable. internal void ExecuteChartAction(Action action) { using (UI.DataVisualization.Charting.Chart chart = new UI.DataVisualization.Charting.Chart()) { chart.Width = new Unit(_width); chart.Height = new Unit(_height); ApplyChartArea(chart); ApplyLegends(chart); ApplySeries(chart); ApplyTitles(chart); DataBindChart(chart); // load the template last so that it can be applied to all the chart elements LoadThemes(chart); action(chart); } } private void LoadThemes(UI.DataVisualization.Charting.Chart chart) { if (!String.IsNullOrEmpty(_theme)) { using (MemoryStream memoryStream = new MemoryStream()) { byte[] themeContent = Encoding.UTF8.GetBytes(_theme); memoryStream.Write(themeContent, 0, themeContent.Length); memoryStream.Seek(0, SeekOrigin.Begin); LoadChartThemeFromFile(chart, memoryStream); } } if (!String.IsNullOrEmpty(_themePath)) { using (Stream stream = _virtualPathProviderFunc().GetFile(_themePath).Open()) { LoadChartThemeFromFile(chart, stream); } } } private static void LoadChartThemeFromFile(UI.DataVisualization.Charting.Chart chart, Stream templateStream) { // workarounds for Chart templating bugs mentioned in: // http://social.msdn.microsoft.com/Forums/en-US/MSWinWebChart/thread/b50d5b7e-30e2-4948-af7a-370d9be1268a chart.Serializer.Content = SerializationContents.All; chart.Serializer.SerializableContent = String.Empty; // deserialize all content chart.Serializer.IsTemplateMode = true; chart.Serializer.IsResetWhenLoading = false; // loading serializer with stream to avoid bug with template file getting locked in VS // The default xml reader used by the serializer does not ignore comments // Using the IsUnknownAttributeIgnored fixes this, but then it would give no feedback to the user // if member names do not match the spelling and casing of Chart properties. XmlReader reader = XmlReader.Create(templateStream, new XmlReaderSettings { IgnoreComments = true }); chart.Serializer.Load(reader); } internal static Chart GetFromCache(HttpContextBase context, string key) { Contract.Assert(context != null); if (String.IsNullOrEmpty(key)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "key"); } var chart = WebCache.Get(key) as Chart; if (chart != null) { chart._httpContext = context; } return chart; } internal static Chart WriteFromCache(HttpContextBase context, string key, string format = "jpeg") { var chart = GetFromCache(context, key); if (chart != null) { chart.Write(format); } return chart; } // Notes on ApplyXXX methods: // Chart elements should be configured before they are added to the chart, otherwise there // will be some rendering problems. // We must catch all exceptions when configuring chart elements and dispose of them manually // if they have not been added to the chart yet, otherwise FxCop will complain. private void ApplyChartArea(UI.DataVisualization.Charting.Chart chart) { ChartArea chartArea = new ChartArea("Default"); try { ApplyAxis(chartArea.AxisX, _xAxis); ApplyAxis(chartArea.AxisY, _yAxis); chart.ChartAreas.Add(chartArea); } catch { // This is to appease FxCop chartArea.Dispose(); throw; } } private static void ApplyAxis(Axis axis, ChartAxisData axisData) { if (axisData == null) { return; } if (!String.IsNullOrEmpty(axisData.Title)) { axis.Title = axisData.Title; } axis.Minimum = axisData.Minimum; axis.Maximum = axisData.Maximum; } private void ApplyLegends(UI.DataVisualization.Charting.Chart chart) { foreach (var legendData in _legends) { var legend = new Legend(); try { legend.Name = legendData.Name ?? String.Empty; legend.Title = legendData.Title ?? String.Empty; } catch (Exception) { // see notes above legend.Dispose(); throw; } chart.Legends.Add(legend); } } [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Target = "yValues, yValuesArray", Justification = "These names make the most sense.")] private void ApplySeries(UI.DataVisualization.Charting.Chart chart) { foreach (var seriesData in _series) { var series = new Series(); try { series.AxisLabel = seriesData.AxisLabel ?? String.Empty; series.ChartArea = seriesData.ChartArea ?? String.Empty; series.ChartType = seriesData.ChartType; series.Legend = seriesData.Legend ?? String.Empty; series.MarkerStep = seriesData.MarkerStep; series.Name = seriesData.Name ?? String.Empty; // data-bind the series (todo - support o.Points.DataBind()) if (seriesData.DataSource != null) { if (String.IsNullOrEmpty(seriesData.DataSource.YFields)) { var yValues = seriesData.DataSource.DataSource; var yValuesArray = yValues as IEnumerable[]; if ((yValuesArray != null) && !(yValues is string[])) { series.Points.DataBindXY(seriesData.DataSource.XDataSource, yValuesArray); } else { series.Points.DataBindXY(seriesData.DataSource.XDataSource, yValues); } } else { series.Points.DataBindXY(seriesData.DataSource.XDataSource, seriesData.DataSource.XField, seriesData.DataSource.DataSource, seriesData.DataSource.YFields); } } } catch (Exception) { // see notes above series.Dispose(); throw; } chart.Series.Add(series); } } private void ApplyTitles(UI.DataVisualization.Charting.Chart chart) { foreach (var titleData in _titles) { var title = new Title(); try { title.Name = titleData.Name; title.Text = titleData.Text; } catch (Exception) { // see notes above title.Dispose(); throw; } chart.Titles.Add(title); } } private static T ConvertStringArgument(string paramName, string value) { object result; if (!ConversionUtil.TryFromString(typeof(T), value, out result)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.Chart_ArgumentConversionFailed, typeof(T).FullName), paramName); } return (T)result; } /// /// Method to convert a string to a ChartImageFormat. /// The chart image needs to be normalized to allow for alternate names such as 'jpg', 'xpng' etc /// to be mapped to their appropriate ChartImageFormat. /// private static ChartImageFormat ConvertStringToChartImageFormat(string format) { object result; format = NormalizeFormat(format); if (!ConversionUtil.TryFromString(typeof(ChartImageFormat), format, out result)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.Image_IncorrectImageFormat, format), "format"); } return (ChartImageFormat)result; } private void DataBindChart(UI.DataVisualization.Charting.Chart chart) { // NOTE: WebForms chart will throw null refs if optional values are set to null if (_dataSource != null) { if (!String.IsNullOrEmpty(_dataSource.GroupByField)) { chart.DataBindCrossTable( _dataSource.DataSource, _dataSource.GroupByField, _dataSource.XField ?? String.Empty, _dataSource.YFields, _dataSource.OtherFields ?? String.Empty, _dataSource.PointSortOrder); } else if (_dataSource.DataBindTable) { chart.DataBindTable( _dataSource.DataSource, _dataSource.XField ?? String.Empty); } else { Debug.Assert(false, "Chart.DataBind was removed - should not reach here"); //chart.DataSource = _dataSource.DataSource; //chart.DataBind(); } } } #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif private static HttpContextBase GetDefaultContext() { return new HttpContextWrapper(HttpContext.Current); } // review: should GUIDs be used in a handler's querystring? private static string GetUniqueKey() { return Guid.NewGuid().ToString(); } private static string NormalizeFormat(string format) { if (String.IsNullOrEmpty(format)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "format"); } if (format.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { format = format.Substring(6); } return ConversionUtil.NormalizeImageFormat(format); } // data-binding can be done through Chart or individual Series private class DataSourceData { public bool DataBindTable { get; set; } public IEnumerable DataSource { get; set; } public string GroupByField { get; set; } public string OtherFields { get; set; } public string XField { get; set; } public string YFields { get; set; } public PointSortOrder PointSortOrder { get; set; } // optional XValue for Series.Points.DataBindXY only: public IEnumerable XDataSource { get; set; } } private class LegendData { public string Name { get; set; } public string Title { get; set; } } private class SeriesData { public string AxisLabel { get; set; } public string ChartArea { get; set; } public SeriesChartType ChartType { get; set; } public string Legend { get; set; } public int MarkerStep { get; set; } public string Name { get; set; } public DataSourceData DataSource { get; set; } } private class TitleData { public string Name { get; set; } public string Text { get; set; } } private class ChartAxisData { public double Minimum { get; set; } public double Maximum { get; set; } public string Title { get; set; } } } } ================================================ FILE: src/System.Web.Helpers/Chart/ChartTheme.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Helpers { public static class ChartTheme { // Review: Need better names. public const string Blue = @" "; public const string Green = @" "; public const string Vanilla = @" "; public const string Vanilla3D = @" "; public const string Yellow = @" "; } } ================================================ FILE: src/System.Web.Helpers/Common/VirtualPathUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; using System.Globalization; using System.IO; using System.Web.Helpers.Resources; using System.Web.WebPages; namespace System.Web.Helpers { internal static class VirtualPathUtil { /// /// Resolves and maps a path (physical or virtual) to a physical path on the server. /// /// The . /// Either a physical rooted path or a virtual path to be mapped. /// Physical paths are returned without modifications. Virtual paths are resolved relative to the current executing page. /// /// Result of this call should not be shown to the user (e.g. in an exception message) since /// it could be security sensitive. But we need to pass this result to the file APIs like File.WriteAllBytes /// which will show it if exceptions are raised from them. Unfortunately VirtualPathProvider doesn't have /// APIs for writing so we can't use that. public static string MapPath(HttpContextBase httpContext, string path) { Debug.Assert(!String.IsNullOrEmpty(path)); if (Path.IsPathRooted(path)) { return path; } // There is no TryMapPath API so we have to catch HttpException if we want to // throw ArgumentException instead. try { return httpContext.Request.MapPath(ResolvePath(TemplateStack.GetCurrentTemplate(httpContext), httpContext, path)); } catch (HttpException) { throw new ArgumentException( String.Format(CultureInfo.InvariantCulture, HelpersResources.PathUtils_IncorrectPath, path), "path"); } } /// /// Resolves path relative to the current executing page /// public static string ResolvePath(string virtualPath) { if (String.IsNullOrEmpty(virtualPath)) { return virtualPath; } if (HttpContext.Current == null) { return virtualPath; } var httpContext = new HttpContextWrapper(HttpContext.Current); return ResolvePath(TemplateStack.GetCurrentTemplate(httpContext), httpContext, virtualPath); } internal static string ResolvePath(ITemplateFile templateFile, HttpContextBase httpContext, string virtualPath) { Debug.Assert(!String.IsNullOrEmpty(virtualPath)); string basePath; if (templateFile != null) { // If a page is available resolve paths relative to it. basePath = templateFile.TemplateInfo.VirtualPath; } else { basePath = httpContext.Request.AppRelativeCurrentExecutionFilePath; } return VirtualPathUtility.Combine(basePath, virtualPath); } } } ================================================ FILE: src/System.Web.Helpers/ConversionUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; using System.Reflection; namespace System.Web.Helpers { internal static class ConversionUtil { private static MethodInfo _stringToEnumMethod; internal static string ToString(T obj) { Type type = typeof(T); if (type.IsEnum) { return obj.ToString(); } TypeConverter converter = TypeDescriptor.GetConverter(type); if ((converter != null) && (converter.CanConvertTo(typeof(string)))) { return converter.ConvertToInvariantString(obj); } return null; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "TypeConverter throws System.Exception instead of a more specific one.")] internal static bool TryFromString(Type type, string value, out object result) { result = null; if (type == typeof(string)) { result = value; return true; } if (type.IsEnum) { return TryFromStringToEnumHelper(type, value, out result); } if (type == typeof(Color)) { Color color; bool rval = TryFromStringToColor(value, out color); result = color; return rval; } // TypeConverter doesn't really have TryConvert APIs. We should avoid TypeConverter.IsValid // which performs a duplicate conversion, and just handle the general exception ourselves. TypeConverter converter = TypeDescriptor.GetConverter(type); if ((converter != null) && converter.CanConvertFrom(typeof(string))) { try { result = converter.ConvertFromInvariantString(value); return true; } catch { // Do nothing } } return false; } internal static bool TryFromStringToEnum(string value, out T result) where T : struct { return Enum.TryParse(value, ignoreCase: true, result: out result); } private static bool TryFromStringToEnumHelper(Type enumType, string value, out object result) { result = null; if (_stringToEnumMethod == null) { _stringToEnumMethod = typeof(ConversionUtil).GetMethod("TryFromStringToEnum", BindingFlags.Static | BindingFlags.NonPublic); Debug.Assert(_stringToEnumMethod != null); } var args = new object[] { value, null }; var rval = (bool)_stringToEnumMethod.MakeGenericMethod(enumType).Invoke(null, args); result = args[1]; return rval; } internal static bool TryFromStringToFontFamily(string fontFamily, out FontFamily result) { result = null; bool converted = false; foreach (FontFamily fontFamilyTemp in FontFamily.Families) { if (fontFamily.Equals(fontFamilyTemp.Name, StringComparison.OrdinalIgnoreCase)) { result = fontFamilyTemp; converted = true; break; } } return converted; } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "TypeConverter throws System.Exception instad of a more specific one.")] internal static bool TryFromStringToColor(string value, out Color result) { result = default(Color); // Parse color specified as hex number if (value.StartsWith("#", StringComparison.OrdinalIgnoreCase)) { // Only allow colors in form of #RRGGBB or #RGB if ((value.Length != 7) && (value.Length != 4)) { return false; } // Expand short version if (value.Length == 4) { char[] newValue = new char[7]; newValue[0] = '#'; newValue[1] = newValue[2] = value[1]; newValue[3] = newValue[4] = value[2]; newValue[5] = newValue[6] = value[3]; value = new string(newValue); } } TypeConverter converter = TypeDescriptor.GetConverter(typeof(Color)); Debug.Assert((converter != null) && (converter.CanConvertFrom(typeof(string)))); // There are no TryConvert APIs on TypeConverter so we have to catch exception. // In addition to that, invalid conversion just throws System.Exception with misleading message, // instead of a more specific exception type. try { result = (Color)converter.ConvertFromInvariantString(value); } catch (Exception) { return false; } return true; } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Format names are used in Http headers and are usually specified in lower case")] internal static string NormalizeImageFormat(string value) { value = value.ToLowerInvariant(); switch (value) { case "jpeg": case "jpg": case "pjpeg": return "jpeg"; case "png": case "x-png": return "png"; case "icon": case "ico": case "x-icon": return "icon"; } return value; } internal static bool TryFromStringToImageFormat(string value, out ImageFormat result) { result = default(ImageFormat); if (String.IsNullOrEmpty(value)) { return false; } if (value.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { value = value.Substring("image/".Length); } value = NormalizeImageFormat(value); TypeConverter converter = TypeDescriptor.GetConverter(typeof(ImageFormat)); Debug.Assert((converter != null) && (converter.CanConvertFrom(typeof(string)))); try { result = (ImageFormat)converter.ConvertFromInvariantString(value); } catch (NotSupportedException) { return false; } return true; } } } ================================================ FILE: src/System.Web.Helpers/Crypto.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Web.Helpers.Resources; namespace System.Web.Helpers { public static class Crypto { private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes private const int PBKDF2SubkeyLength = 256 / 8; // 256 bits private const int SaltSize = 128 / 8; // 128 bits [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "byte", Justification = "It really is a byte length")] internal static byte[] GenerateSaltInternal(int byteLength = SaltSize) { byte[] buf = new byte[byteLength]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(buf); } return buf; } [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "byte", Justification = "It really is a byte length")] public static string GenerateSalt(int byteLength = SaltSize) { return Convert.ToBase64String(GenerateSaltInternal(byteLength)); } public static string Hash(string input, string algorithm = "sha256") { if (input == null) { throw new ArgumentNullException("input"); } return Hash(Encoding.UTF8.GetBytes(input), algorithm); } public static string Hash(byte[] input, string algorithm = "sha256") { if (input == null) { throw new ArgumentNullException("input"); } using (HashAlgorithm alg = HashAlgorithm.Create(algorithm)) { if (alg != null) { byte[] hashData = alg.ComputeHash(input); return BinaryToHex(hashData); } else { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, HelpersResources.Crypto_NotSupportedHashAlg, algorithm)); } } } [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SHA", Justification = "Consistent with the Framework, which uses SHA")] public static string SHA1(string input) { return Hash(input, "sha1"); } [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SHA", Justification = "Consistent with the Framework, which uses SHA")] public static string SHA256(string input) { return Hash(input, "sha256"); } /* ======================= * HASHED PASSWORD FORMATS * ======================= * * Version 0: * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. * (See also: SDL crypto guidelines v5.1, Part III) * Format: { 0x00, salt, subkey } */ public static string HashPassword(string password) { if (password == null) { throw new ArgumentNullException("password"); } // Produce a version 0 (see comment above) password hash. byte[] salt; byte[] subkey; using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount)) { salt = deriveBytes.Salt; subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength); } byte[] outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength]; Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize); Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength); return Convert.ToBase64String(outputBytes); } // hashedPassword must be of the format of HashWithPassword (salt + Hash(salt+input) public static bool VerifyHashedPassword(string hashedPassword, string password) { if (hashedPassword == null) { throw new ArgumentNullException("hashedPassword"); } if (password == null) { throw new ArgumentNullException("password"); } byte[] hashedPasswordBytes = Convert.FromBase64String(hashedPassword); // Verify a version 0 (see comment above) password hash. if (hashedPasswordBytes.Length != (1 + SaltSize + PBKDF2SubkeyLength) || hashedPasswordBytes[0] != 0x00) { // Wrong length or version header. return false; } byte[] salt = new byte[SaltSize]; Buffer.BlockCopy(hashedPasswordBytes, 1, salt, 0, SaltSize); byte[] storedSubkey = new byte[PBKDF2SubkeyLength]; Buffer.BlockCopy(hashedPasswordBytes, 1 + SaltSize, storedSubkey, 0, PBKDF2SubkeyLength); byte[] generatedSubkey; using (var deriveBytes = new Rfc2898DeriveBytes(password, salt, PBKDF2IterCount)) { generatedSubkey = deriveBytes.GetBytes(PBKDF2SubkeyLength); } return ByteArraysEqual(storedSubkey, generatedSubkey); } internal static string BinaryToHex(byte[] data) { char[] hex = new char[data.Length * 2]; for (int iter = 0; iter < data.Length; iter++) { byte hexChar = ((byte)(data[iter] >> 4)); hex[iter * 2] = (char)(hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30); hexChar = ((byte)(data[iter] & 0xF)); hex[(iter * 2) + 1] = (char)(hexChar > 9 ? hexChar + 0x37 : hexChar + 0x30); } return new string(hex); } // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized. [MethodImpl(MethodImplOptions.NoOptimization)] private static bool ByteArraysEqual(byte[] a, byte[] b) { if (ReferenceEquals(a, b)) { return true; } if (a == null || b == null || a.Length != b.Length) { return false; } bool areSame = true; for (int i = 0; i < a.Length; i++) { areSame &= (a[i] == b[i]); } return areSame; } } } ================================================ FILE: src/System.Web.Helpers/DynamicHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; using System.Runtime.CompilerServices; using Microsoft.CSharp.RuntimeBinder; namespace Microsoft.Internal.Web.Utils { /// /// Helper to evaluate different method on dynamic objects /// internal static class DynamicHelper { // We must pass in "object" instead of "dynamic" for the target dynamic object because if we use dynamic, the compiler will // convert the call to this helper into a dynamic expression, even though we don't need it to be. Since this class is internal, // it cannot be accessed from a dynamic expression and thus we get errors. // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details public static bool TryGetMemberValue(object obj, string memberName, out object result) { try { result = GetMemberValue(obj, memberName); return true; } catch (RuntimeBinderException) { } catch (RuntimeBinderInternalCompilerException) { } // We catch the C# specific runtime binder exceptions since we're using the C# binder in this case result = null; return false; } // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to swallow exceptions that happen during runtime binding")] public static bool TryGetMemberValue(object obj, GetMemberBinder binder, out object result) { try { // VB us an instance of GetBinderAdapter that does not implement FallbackGetMemeber. This causes lookup of property expressions on dynamic objects to fail. // Since all types are private to the assembly, we assume that as long as they belong to CSharp runtime, it is the right one. if (typeof(Binder).Assembly.Equals(binder.GetType().Assembly)) { // Only use the binder if its a C# binder. result = GetMemberValue(obj, binder); } else { result = GetMemberValue(obj, binder.Name); } return true; } catch { result = null; return false; } } // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details public static object GetMemberValue(object obj, string memberName) { var callSite = GetMemberAccessCallSite(memberName); return callSite.Target(callSite, obj); } // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details public static object GetMemberValue(object obj, GetMemberBinder binder) { var callSite = GetMemberAccessCallSite(binder); return callSite.Target(callSite, obj); } // dynamic d = new object(); // object s = d.Name; // The following code gets generated for this expression: // callSite = CallSite>.Create(Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); // callSite.Target(callSite, d); // typeof(Program) is the containing type of the dynamic operation. // Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details public static CallSite> GetMemberAccessCallSite(string memberName) { var binder = Binder.GetMember(CSharpBinderFlags.None, memberName, typeof(DynamicHelper), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); return GetMemberAccessCallSite(binder); } // Dev10 Bug 914027 - Changed the callsite's target parameter from dynamic to object, see comment at top for details public static CallSite> GetMemberAccessCallSite(CallSiteBinder binder) { return CallSite>.Create(binder); } // Dev10 Bug 914027 - Changed the first parameter from dynamic to object, see comment at top for details public static IEnumerable GetMemberNames(object obj) { var provider = obj as IDynamicMetaObjectProvider; Debug.Assert(provider != null, "obj doesn't implement IDynamicMetaObjectProvider"); Expression parameter = Expression.Parameter(typeof(object)); return provider.GetMetaObject(parameter).GetDynamicMemberNames(); } } } ================================================ FILE: src/System.Web.Helpers/DynamicJavaScriptConverter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Web.Script.Serialization; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { /// /// Converter that knows how to get the member values from a dynamic object. /// internal class DynamicJavaScriptConverter : JavaScriptConverter { public override IEnumerable SupportedTypes { get { // REVIEW: For some reason the converters don't pick up interfaces yield return typeof(IDynamicMetaObjectProvider); yield return typeof(DynamicObject); } } public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) { throw new NotSupportedException(); } public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) { var values = new Dictionary(StringComparer.OrdinalIgnoreCase); var memberNames = DynamicHelper.GetMemberNames(obj); // This should never happen Debug.Assert(memberNames != null); // Get the value for each member in the dynamic object foreach (string memberName in memberNames) { object value = DynamicHelper.GetMemberValue(obj, memberName); var jsonValue = value as DynamicJsonArray; if (jsonValue != null) { value = (object[])jsonValue; } values[memberName] = value; } return values; } } } ================================================ FILE: src/System.Web.Helpers/DynamicJsonArray.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; namespace System.Web.Helpers { [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This class isn't meant to be used directly")] public class DynamicJsonArray : DynamicObject, IEnumerable { private readonly object[] _arrayValues; public DynamicJsonArray(object[] arrayValues) { Debug.Assert(arrayValues != null); _arrayValues = arrayValues.Select(Json.WrapObject).ToArray(); } public int Length { get { return _arrayValues.Length; } } public dynamic this[int index] { get { return _arrayValues[index]; } set { _arrayValues[index] = Json.WrapObject(value); } } public override bool TryConvert(ConvertBinder binder, out object result) { if (_arrayValues.GetType().IsAssignableFrom(binder.Type)) { result = _arrayValues; return true; } return base.TryConvert(binder, out result); } public override bool TryGetMember(GetMemberBinder binder, out object result) { // Testing for members should never throw. This is important when dealing with // services that return different json results. Testing for a member shouldn't throw, // it should just return null (or undefined) result = null; return true; } public IEnumerator GetEnumerator() { return _arrayValues.GetEnumerator(); } private IEnumerable GetEnumerable() { return _arrayValues.AsEnumerable(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerable().GetEnumerator(); } [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")] public static implicit operator object[](DynamicJsonArray obj) { return obj._arrayValues; } [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "This class isn't meant to be used directly")] public static implicit operator Array(DynamicJsonArray obj) { return obj._arrayValues; } } } ================================================ FILE: src/System.Web.Helpers/DynamicJsonObject.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Globalization; using System.Linq; using System.Web.Helpers.Resources; namespace System.Web.Helpers { // REVIEW: Consider implementing ICustomTypeDescriptor and IDictionary public class DynamicJsonObject : DynamicObject { private readonly IDictionary _values; public DynamicJsonObject(IDictionary values) { Debug.Assert(values != null); _values = values.ToDictionary(p => p.Key, p => Json.WrapObject(p.Value), StringComparer.OrdinalIgnoreCase); } public override bool TryConvert(ConvertBinder binder, out object result) { result = null; if (binder.Type.IsAssignableFrom(_values.GetType())) { result = _values; } else { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, HelpersResources.Json_UnableToConvertType, binder.Type)); } return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = GetValue(binder.Name); return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { _values[binder.Name] = Json.WrapObject(value); return true; } public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { string key = GetKey(indexes); if (!String.IsNullOrEmpty(key)) { _values[key] = Json.WrapObject(value); } return true; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { string key = GetKey(indexes); result = null; if (!String.IsNullOrEmpty(key)) { result = GetValue(key); } return true; } private static string GetKey(object[] indexes) { if (indexes.Length == 1) { return (string)indexes[0]; } // REVIEW: Should this throw? return null; } public override IEnumerable GetDynamicMemberNames() { return _values.Keys; } private object GetValue(string name) { object result; if (_values.TryGetValue(name, out result)) { return result; } return null; } } } ================================================ FILE: src/System.Web.Helpers/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "sha", Scope = "resource", Target = "System.Web.Helpers.Resources.HelpersResources.resources", Justification = "sha is the algorithm")] ================================================ FILE: src/System.Web.Helpers/HtmlElement.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Web.UI; namespace System.Web.Helpers { internal class HtmlElement { public HtmlElement(string tagName) { TagName = tagName; Attributes = new Dictionary(); Children = new List(); } internal string TagName { get; set; } internal string InnerText { get; set; } public IList Children { get; set; } private IDictionary Attributes { get; set; } public string this[string name] { get { return Attributes[name]; } set { MergeAttribute(name, value); } } public HtmlElement SetInnerText(string innerText) { InnerText = innerText; Children.Clear(); return this; } public HtmlElement AppendChild(HtmlElement e) { Children.Add(e); return this; } public HtmlElement AppendChild(string innerText) { AppendChild(CreateSpan(innerText)); return this; } private void MergeAttribute(string name, string value) { Attributes[name] = value; } public HtmlElement AddCssClass(string className) { string currentValue; if (!Attributes.TryGetValue("class", out currentValue)) { Attributes["class"] = className; } else { Attributes["class"] = currentValue + " " + className; } return this; } public IHtmlString ToHtmlString() { using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) { WriteTo(sw); return new HtmlString(sw.ToString()); } } public void WriteTo(TextWriter writer) { WriteToInternal(new HtmlTextWriter(writer)); } private void WriteToInternal(HtmlTextWriter writer) { foreach (var a in Attributes) { writer.AddAttribute(a.Key, a.Value, true); } writer.RenderBeginTag(TagName); if (!String.IsNullOrEmpty(InnerText)) { writer.WriteEncodedText(InnerText); } else { foreach (var e in Children) { e.WriteToInternal(writer); } } writer.RenderEndTag(); } public override string ToString() { return ToHtmlString().ToString(); } internal static HtmlElement CreateSpan(string innerText, string cssClass = null) { var span = new HtmlElement("span"); span.SetInnerText(innerText); if (!String.IsNullOrEmpty(cssClass)) { span.AddCssClass(cssClass); } return span; } } } ================================================ FILE: src/System.Web.Helpers/HtmlObjectPrinter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Web.Helpers.Resources; namespace System.Web.Helpers { internal class HtmlObjectPrinter : ObjectVisitor { private const string Styles = @" "; private static readonly HtmlElement _nullSpan = HtmlElement.CreateSpan("(null)", "null"); // List of chars to escape within strings private static readonly Dictionary _printableEscapeChars = new Dictionary { { '\0', "\\0" }, { '\\', "\\\\" }, { '\'', "'" }, { '\"', "\\\"" }, { '\a', "\\a" }, { '\b', "\\b" }, { '\f', "\\f" }, { '\n', "\\n" }, { '\r', "\\r" }, { '\t', "\\t" }, { '\v', "\\v" }, }; // We want to exclude the type name next to the value for members private bool _excludeTypeName; private Stack _elementStack = new Stack(); public HtmlObjectPrinter(int recursionLimit, int enumerationLimit) : base(recursionLimit, enumerationLimit) { } private HtmlElement Current { get { Debug.Assert(_elementStack.Count > 0); return _elementStack.Peek(); } } public void WriteTo(object value, TextWriter writer) { HtmlElement rootElement = new HtmlElement("div"); rootElement.AddCssClass("objectinfo"); PushElement(rootElement); Visit(value, 0); PopElement(); Debug.Assert(_elementStack.Count == 0, "Stack should be empty"); // REVIEW: We should only do this once per page/request writer.Write(Styles); rootElement.WriteTo(writer); } public override void VisitKeyValues(object value, IEnumerable keys, Func valueSelector, int depth) { string id = GetObjectId(value); HtmlElement ul = new HtmlElement("ul"); ul.AddCssClass("typeEnumeration"); ul["id"] = id; PushElement(ul); base.VisitKeyValues(value, keys, valueSelector, depth); PopElement(); Current.AppendChild(ul); } public override void VisitKeyValue(object key, object value, int depth) { HtmlElement keyElement = new HtmlElement("span"); PushElement(keyElement); Visit(key, depth); PopElement(); HtmlElement valueElement = new HtmlElement("span"); PushElement(valueElement); Visit(value, depth); PopElement(); // Append the elements to the li HtmlElement li = new HtmlElement("li"); li.AppendChild(keyElement); li.AppendChild(" = "); li.AppendChild(valueElement); Current.AppendChild(li); } public override void VisitEnumerable(IEnumerable enumerable, int depth) { string id = GetObjectId(enumerable); HtmlElement ul = new HtmlElement("ul"); ul.AddCssClass("typeEnumeration"); ul["id"] = id; PushElement(ul); base.VisitEnumerable(enumerable, depth); PopElement(); Current.AppendChild(ul); } public override void VisitIndexedEnumeratedValue(int index, object item, int depth) { HtmlElement li = new HtmlElement("li"); li.AppendChild(String.Format(CultureInfo.InvariantCulture, "[{0}] = ", index)); PushElement(li); base.VisitIndexedEnumeratedValue(index, item, depth); PopElement(); Current.AppendChild(li); } public override void VisitEnumeratedValue(object item, int depth) { HtmlElement li = new HtmlElement("li"); PushElement(li); base.VisitEnumeratedValue(item, depth); PopElement(); Current.AppendChild(li); } public override void VisitEnumeratonLimitExceeded() { HtmlElement li = new HtmlElement("li"); li.AppendChild("..."); Current.AppendChild(li); } public override void VisitMembers(IEnumerable names, Func typeSelector, Func valueSelector, int depth) { HtmlElement ul = new HtmlElement("ul"); ul.AddCssClass("typeProperties"); PushElement(ul); base.VisitMembers(names, typeSelector, valueSelector, depth); PopElement(); Current.AppendChild(ul); } public override void VisitMember(string name, Type type, object value, int depth) { HtmlElement li = new HtmlElement("li"); if (type != null) { li.AppendChild(CreateTypeNameSpan(type)); li.AppendChild(" "); } li.AppendChild(CreateNameSpan(name)); li.AppendChild(" = "); PushElement(li); _excludeTypeName = true; base.VisitMember(name, type, value, depth); _excludeTypeName = false; PopElement(); Current.AppendChild(li); } public override void VisitComplexObject(object value, int depth) { string id = GetObjectId(value); HtmlElement objectElement = new HtmlElement("div"); objectElement.AddCssClass("typeContainer"); objectElement["id"] = id; PushElement(objectElement); base.VisitComplexObject(value, depth); PopElement(); if (objectElement.Children.Any()) { Current.AppendChild(objectElement); } } public override void VisitNull() { Current.AppendChild(_nullSpan); } public override void VisitStringValue(string stringValue) { // Convert the string escape sequences stringValue = "\"" + ConvertEscapseSequences(stringValue) + "\""; Current.AppendChild(CreateQuotedSpan(stringValue)); } public override void VisitVisitedObject(string id, object value) { Current.AppendChild(CreateVisitedLink(id)); } public override void Visit(object value, int depth) { if (value != null) { if (!_excludeTypeName) { Current.AppendChild(CreateTypeNameSpan(value.GetType())); Current.AppendChild(" "); } _excludeTypeName = false; } base.Visit(value, depth); } public override void VisitObjectVisitorException(ObjectVisitorException exception) { Current.AppendChild(CreateExceptionSpan(exception)); } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Making the value lowercase has nothing to do with normalization. It's used to show true or false instead of the Title case version")] public override void VisitConvertedValue(object value, string convertedValue) { Type type = value.GetType(); if (type.Equals(typeof(bool))) { // Convert True or False to lowercase convertedValue = convertedValue.ToLowerInvariant(); Current.AppendChild(CreateTypeSpan(convertedValue)); return; } if (type.Equals(typeof(char))) { string charValue = GetCharValue((char)value); Current.AppendChild(CreateQuotedSpan("'" + charValue + "'")); return; } // See if the value is a Type itself Type valueAsType = value as Type; if (valueAsType != null) { // For types we're going to generate elements that print typeof(TypeName) Current.AppendChild(CreateParentSpan(CreateTypeSpan("typeof"), CreateOperatorSpan("("), CreateTypeNameSpan(valueAsType), CreateOperatorSpan(")"))); } else { Current.AppendChild(CreateValueSpan(convertedValue)); } } private static HtmlElement CreateParentSpan(params HtmlElement[] elements) { HtmlElement span = new HtmlElement("span"); foreach (var e in elements) { span.AppendChild(e); } return span; } private static HtmlElement CreateNameSpan(string name) { return HtmlElement.CreateSpan(name, "name"); } private static HtmlElement CreateOperatorSpan(string @operator) { return HtmlElement.CreateSpan(@operator, "operator"); } private static HtmlElement CreateValueSpan(string value) { return HtmlElement.CreateSpan(value, "value"); } private static HtmlElement CreateExceptionSpan(ObjectVisitorException exception) { HtmlElement span = new HtmlElement("span"); span.AppendChild(HelpersResources.ObjectInfo_PropertyThrewException); span.AppendChild(HtmlElement.CreateSpan(exception.InnerException.Message, "exception")); return span; } private static HtmlElement CreateQuotedSpan(string value) { return HtmlElement.CreateSpan(value, "quote"); } private static HtmlElement CreateLink(string href, string linkText, string cssClass = null) { HtmlElement a = new HtmlElement("a"); a.SetInnerText(linkText); a["href"] = href; if (!String.IsNullOrEmpty(cssClass)) { a.AddCssClass(cssClass); } return a; } private static HtmlElement CreateVisitedLink(string id) { string text = String.Format(CultureInfo.InvariantCulture, "[{0}]", HelpersResources.ObjectInfo_PreviousDisplayed); return CreateLink("#" + id, text); } private static HtmlElement CreateTypeSpan(string value) { return HtmlElement.CreateSpan(value, "type"); } private static HtmlElement CreateTypeNameSpan(Type type) { string typeName = GetTypeName(type); HtmlElement span = new HtmlElement("span"); StringBuilder sb = new StringBuilder(); // Convert the type name into html elements with different css classes foreach (var ch in typeName) { if (IsOperator(ch)) { if (sb.Length > 0) { span.AppendChild(CreateTypeSpan(sb.ToString())); sb.Clear(); } span.AppendChild(CreateOperatorSpan(ch.ToString())); } else { sb.Append(ch); } } if (sb.Length > 0) { span.AppendChild(CreateTypeSpan(sb.ToString())); } return span; } private static bool IsOperator(char ch) { // These are the operators we expect to see within type names return ch == '[' || ch == ']' || ch == '<' || ch == '>' || ch == '&' || ch == '*'; } internal void PushElement(HtmlElement element) { _elementStack.Push(element); } internal HtmlElement PopElement() { Debug.Assert(_elementStack.Count > 0); return _elementStack.Pop(); } internal static string ConvertEscapseSequences(string value) { StringBuilder sb = new StringBuilder(); foreach (var ch in value) { sb.Append(GetCharValue(ch)); } return sb.ToString(); } private static string GetCharValue(char ch) { string value; if (_printableEscapeChars.TryGetValue(ch, out value)) { return value; } // REVIEW: Perf? return ch.ToString(); } } } ================================================ FILE: src/System.Web.Helpers/Json.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Web.Script.Serialization; namespace System.Web.Helpers { public static class Json { private static readonly JavaScriptSerializer _serializer = CreateSerializer(); public static string Encode(object value) { // Serialize our dynamic array type as an array DynamicJsonArray jsonArray = value as DynamicJsonArray; if (jsonArray != null) { return _serializer.Serialize((object[])jsonArray); } return _serializer.Serialize(value); } public static void Write(object value, TextWriter writer) { writer.Write(_serializer.Serialize(value)); } public static dynamic Decode(string value) { return WrapObject(_serializer.DeserializeObject(value)); } public static dynamic Decode(string value, Type targetType) { return WrapObject(_serializer.Deserialize(value, targetType)); } public static T Decode(string value) { return _serializer.Deserialize(value); } private static JavaScriptSerializer CreateSerializer() { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new[] { new DynamicJavaScriptConverter() }); return serializer; } internal static dynamic WrapObject(object value) { // The JavaScriptSerializer returns IDictionary for objects // and object[] for arrays, so we wrap those in different dynamic objects // so we can access the object graph using dynamic var dictionaryValues = value as IDictionary; if (dictionaryValues != null) { return new DynamicJsonObject(dictionaryValues); } var arrayValues = value as object[]; if (arrayValues != null) { return new DynamicJsonArray(arrayValues); } return value; } } } ================================================ FILE: src/System.Web.Helpers/ObjectInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web.WebPages; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { public static class ObjectInfo { private const int DefaultRecursionLimit = 10; private const int DefaultEnumerationLimit = 1000; public static HelperResult Print(object value, int depth = DefaultRecursionLimit, int enumerationLength = DefaultEnumerationLimit) { if (depth < 0) { throw new ArgumentOutOfRangeException( "depth", String.Format(CultureInfo.InvariantCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 0)); } if (enumerationLength <= 0) { throw new ArgumentOutOfRangeException( "enumerationLength", String.Format(CultureInfo.InvariantCulture, CommonResources.Argument_Must_Be_GreaterThan, 0)); } HtmlObjectPrinter printer = new HtmlObjectPrinter(depth, enumerationLength); return new HelperResult(writer => printer.WriteTo(value, writer)); } } } ================================================ FILE: src/System.Web.Helpers/ObjectVisitor.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { internal class ObjectVisitor { private static readonly Dictionary _typeNames = new Dictionary { { typeof(string), "string" }, { typeof(object), "object" }, { typeof(int), "int" }, { typeof(byte), "byte" }, { typeof(short), "short" }, { typeof(long), "long" }, { typeof(decimal), "decimal" }, { typeof(float), "float" }, { typeof(double), "double" }, { typeof(bool), "bool" }, { typeof(char), "char" }, { typeof(void), "void" } }; private static readonly char[] _separators = { '&', '[', '*' }; private readonly int _recursionLimit; private readonly int _enumerationLimit; private Dictionary _visited = new Dictionary(); public ObjectVisitor(int recursionLimit, int enumerationLimit) { Debug.Assert(enumerationLimit > 0); Debug.Assert(recursionLimit >= 0); _enumerationLimit = enumerationLimit; _recursionLimit = recursionLimit; } protected string GetObjectId(object value) { string id; if (_visited.TryGetValue(value, out id)) { return id; } return null; } public virtual void Visit(object value, int depth) { if (value == null || DBNull.Value.Equals(value)) { VisitNull(); return; } // Check to see if the we've already visited this object string id; if (_visited.TryGetValue(value, out id)) { VisitVisitedObject(id, value); return; } string stringValue = value as string; if (stringValue != null) { VisitStringValue(stringValue); return; } if (TryConvertToString(value, out stringValue)) { VisitConvertedValue(value, stringValue); return; } // This exceptin occurs when we try to access the property and it fails // for some reason. The actual exception is wrapped in the ObjectVisitorException ObjectVisitorException exception = value as ObjectVisitorException; if (exception != null) { VisitObjectVisitorException(exception); return; } // Mark the object as visited id = CreateObjectId(value); _visited.Add(value, id); NameValueCollection nameValueCollection = value as NameValueCollection; if (nameValueCollection != null) { VisitNameValueCollection(nameValueCollection, depth); return; } IDictionary dictionary = value as IDictionary; if (dictionary != null) { VisitDictionary(dictionary, depth); return; } IEnumerable enumerable = value as IEnumerable; if (enumerable != null) { VisitEnumerable(enumerable, depth); return; } VisitComplexObject(value, depth + 1); } public virtual void VisitObjectVisitorException(ObjectVisitorException exception) { } public virtual void VisitConvertedValue(object value, string convertedValue) { VisitStringValue(convertedValue); } public virtual void VisitVisitedObject(string id, object value) { } public virtual void VisitNull() { } public virtual void VisitStringValue(string stringValue) { } public virtual void VisitComplexObject(object value, int depth) { if (depth > _recursionLimit) { return; } Debug.Assert(value != null, "Value should not be null"); var dynamicObject = value as IDynamicMetaObjectProvider; // Only look at dynamic objects that do not implement ICustomTypeDescriptor if (dynamicObject != null && !(dynamicObject is ICustomTypeDescriptor)) { var memberNames = DynamicHelper.GetMemberNames(dynamicObject); if (memberNames != null) { // Always use the runtime type for dynamic objects since there is no metadata VisitMembers(memberNames, name => null, name => DynamicHelper.GetMemberValue(dynamicObject, name), depth); } } else { // REVIEW: We should try to filter out properties of certain types // Dump properties using type descriptor var props = TypeDescriptor.GetProperties(value); var propNames = from PropertyDescriptor p in props select p.Name; VisitMembers(propNames, name => props.Find(name, ignoreCase: true).PropertyType, name => GetPropertyDescriptorValue(value, name, props), depth); // Dump fields var fields = value.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) .ToDictionary(field => field.Name); VisitMembers(fields.Keys, name => fields[name].FieldType, name => GetFieldValue(value, name, fields), depth); } } public virtual void VisitNameValueCollection(NameValueCollection collection, int depth) { VisitKeyValues(collection, collection.AllKeys.Cast(), key => collection[(string)key], depth); } public virtual void VisitDictionary(IDictionary dictionary, int depth) { VisitKeyValues(dictionary, dictionary.Keys.Cast(), key => dictionary[key], depth); } public virtual void VisitEnumerable(IEnumerable enumerable, int depth) { if (depth > _recursionLimit) { return; } Type enumerableType = enumerable.GetType(); bool isIndexedEnumeration = ImplementsInterface(enumerableType, typeof(IList<>)) || ImplementsInterface(enumerableType, typeof(IList)); int index = 0; foreach (var item in enumerable) { if (index >= _enumerationLimit) { VisitEnumeratonLimitExceeded(); break; } if (isIndexedEnumeration) { VisitIndexedEnumeratedValue(index, item, depth); } else { VisitEnumeratedValue(item, depth); } index++; } } public virtual void VisitEnumeratedValue(object item, int depth) { Visit(item, depth); } public virtual void VisitIndexedEnumeratedValue(int index, object item, int depth) { Visit(item, depth); } public virtual void VisitEnumeratonLimitExceeded() { } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't want to fail surface any exceptions throw from getting property accessors")] public virtual void VisitMembers(IEnumerable names, Func typeSelector, Func valueSelector, int depth) { foreach (string name in names) { Type type = null; object value = null; try { // Get the type and value type = typeSelector(name); value = valueSelector(name); // If the type is null try using the runtime type if (value != null && type == null) { type = value.GetType(); } } catch (Exception ex) { // Set the value as an exception we know about value = new ObjectVisitorException(null, ex); } finally { VisitMember(name, type, value, depth); } } } public virtual void VisitMember(string name, Type type, object value, int depth) { Visit(value, depth); } public virtual void VisitKeyValues(object value, IEnumerable keys, Func valueSelector, int depth) { if (depth > _recursionLimit) { return; } foreach (var key in keys) { VisitKeyValue(key, valueSelector(key), depth); } } public virtual void VisitKeyValue(object key, object value, int depth) { // Dump the key and value Visit(key, depth); Visit(value, depth); } protected virtual string CreateObjectId(object value) { // REVIEW: Maybe use a guid? return value.GetHashCode().ToString(CultureInfo.InvariantCulture); } internal static string GetTypeName(Type type) { // See if we have the type name stored string typeName; if (_typeNames.TryGetValue(type, out typeName)) { return typeName; } if (type.IsGenericType) { // Get the generic type name without arguments string genericTypeName = GetGenericTypeName(type); // Create a user friendly type name var arguments = from argType in type.GetGenericArguments() select GetTypeName(argType); return String.Format(CultureInfo.InvariantCulture, "{0}<{1}>", genericTypeName, String.Join(", ", arguments)); } if (type.IsByRef || type.IsArray || type.IsPointer) { // Get the element type name string elementTypeName = GetTypeName(type.GetElementType()); // Append the separator int sepIndex = type.Name.IndexOfAny(_separators); return elementTypeName + type.Name.Substring(sepIndex); } // Fallback to using the type name as is return type.Name; } private static string GetGenericTypeName(Type type) { Debug.Assert(type.IsGenericType, "Type is not a generic type"); // Check for anonymous types if (IsAnonymousType(type)) { return "AnonymousType"; } string genericTypeDefinitionName = type.GetGenericTypeDefinition().Name; int index = genericTypeDefinitionName.IndexOf('`'); Debug.Assert(index >= 0); // Get the generic type name without the ` return genericTypeDefinitionName.Substring(0, index); } // Copied from System.Web.WebPages/Util/TypeHelpers.cs private static bool IsAnonymousType(Type type) { Debug.Assert(type != null, "Type should not be null"); // TODO: The only way to detect anonymous types right now. return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) && type.IsGenericType && type.Name.Contains("AnonymousType") && (type.Name.StartsWith("<>", StringComparison.OrdinalIgnoreCase) || type.Name.StartsWith("VB$", StringComparison.OrdinalIgnoreCase)) && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; } private static bool ImplementsInterface(Type type, Type targetInterfaceType) { Func implementsInterface = t => targetInterfaceType.IsAssignableFrom(t); if (targetInterfaceType.IsGenericType) { implementsInterface = t => t.IsGenericType && targetInterfaceType.IsAssignableFrom(t.GetGenericTypeDefinition()); } return implementsInterface(type) || type.GetInterfaces().Any(implementsInterface); } private static object GetFieldValue(object value, string name, IDictionary fields) { FieldInfo fieldInfo; // Get the value from the dictionary bool result = fields.TryGetValue(name, out fieldInfo); Debug.Assert(result, "Entry should exist"); return fieldInfo.GetValue(value); } private static object GetPropertyDescriptorValue(object value, string name, PropertyDescriptorCollection props) { PropertyDescriptor propertyDescriptor = props.Find(name, ignoreCase: true); Debug.Assert(propertyDescriptor != null, "Property descriptor shouldn't be null"); return propertyDescriptor.GetValue(value); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't want to surface any exceptions while trying to convert from string")] private static bool TryConvertToString(object value, out string stringValue) { stringValue = null; try { IConvertible convertibe = value as IConvertible; if (convertibe != null) { stringValue = convertibe.ToString(CultureInfo.CurrentCulture); return true; } TypeConverter converter = TypeDescriptor.GetConverter(value); if (converter.CanConvertFrom(typeof(string))) { stringValue = converter.ConvertToString(value); return true; } Type type = value.GetType(); if (type == typeof(object)) { stringValue = value.ToString(); return true; } Type valueAsType = value as Type; if (valueAsType != null) { stringValue = "typeof(" + GetTypeName(valueAsType) + ")"; return true; } } catch (Exception) { // If we failed to convert the type for any reason return false } return false; } [Serializable] public class ObjectVisitorException : Exception { public ObjectVisitorException() { } public ObjectVisitorException(string message) : base(message) { } public ObjectVisitorException(string message, Exception inner) : base(message, inner) { } protected ObjectVisitorException( SerializationInfo info, StreamingContext context) : base(info, context) { } } } } ================================================ FILE: src/System.Web.Helpers/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("System.Web.Helpers")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("System.Web.Helpers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] ================================================ FILE: src/System.Web.Helpers/Resources/ChartTemplates.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Microsoft.WebPages.Helpers.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class ChartTemplates { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal ChartTemplates() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.WebPages.Helpers.Resources.ChartTemplates", typeof(ChartTemplates).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to - <Chart BackColor="#D3DFF0" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="26, 59, 105" BorderlineDashStyle="Solid" BorderWidth="2" Palette="BrightPastel"> ///- <ChartAreas> /// <ChartArea Name="Default" _Template_="All" BackColor="64, 165, 191, 228" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent" /> /// </ChartAreas> /// <Legends> /// <Legend _Template_="All" BackColor="Transparent" Font="Trebuchet MS, [rest of string was truncated]";. /// internal static string Blue { get { return ResourceManager.GetString("Blue", resourceCulture); } } /// /// Looks up a localized string similar to <Chart BackColor="#C9DC87" BackGradientStyle="TopBottom" BorderColor="181, 64, 1" BorderWidth="2" BorderlineDashStyle="Solid" Palette="BrightPastel"> /// <ChartAreas> /// <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> /// <AxisY LineColor="64, 64, 64, 64"> /// <MajorGrid Interval="Auto" LineColor="64, 64, 64, 64" /> /// <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold [rest of string was truncated]";. /// internal static string Green { get { return ResourceManager.GetString("Green", resourceCulture); } } /// /// Looks up a localized string similar to <Chart Palette="SemiTransparent" BorderColor="#000" BorderWidth="2" BorderlineDashStyle="Solid"> ///<ChartAreas> /// <ChartArea _Template_="All" Name="Default"> /// <AxisX> /// <MinorGrid Enabled="False" /> /// <MajorGrid Enabled="False" /> /// </AxisX> /// <AxisY> /// <MajorGrid Enabled="False" /> /// <MinorGrid Enabled="False" /> /// </AxisY> /// </ChartArea> ///</ChartAreas> ///</Chart>. /// internal static string Vanilla { get { return ResourceManager.GetString("Vanilla", resourceCulture); } } /// /// Looks up a localized string similar to <Chart BackColor="#555" BackGradientStyle="TopBottom" BorderColor="181, 64, 1" BorderWidth="2" BorderlineDashStyle="Solid" Palette="Excel" AntiAliasing="All"> /// <ChartAreas> /// <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> /// <Area3DStyle LightStyle="Simplistic" Enable3D="True" Inclination="5" IsClustered="False" IsRightAngleAxes="False" Perspective="10" Rotation [rest of string was truncated]";. /// internal static string Vanilla3D { get { return ResourceManager.GetString("Vanilla3D", resourceCulture); } } /// /// Looks up a localized string similar to <Chart BackColor="#FADA5E" BackGradientStyle="TopBottom" BorderColor="#B8860B" BorderWidth="2" BorderlineDashStyle="Solid" Palette="EarthTones"> /// <ChartAreas> /// <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> /// <AxisY> /// <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold" /> /// </AxisY> /// <AxisX LineColor="64, 64, 64, 64"> /// <LabelStyle Font="Trebuch [rest of string was truncated]";. /// internal static string Yellow { get { return ResourceManager.GetString("Yellow", resourceCulture); } } } } ================================================ FILE: src/System.Web.Helpers/Resources/ChartTemplates.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 <Chart BackColor="#D3DFF0" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="26, 59, 105" BorderlineDashStyle="Solid" BorderWidth="2" Palette="BrightPastel"> <ChartAreas> <ChartArea Name="Default" _Template_="All" BackColor="64, 165, 191, 228" BackGradientStyle="TopBottom" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent" /> </ChartAreas> <Legends> <Legend _Template_="All" BackColor="Transparent" Font="Trebuchet MS, 8.25pt, style=Bold" IsTextAutoFit="False" /> </Legends> <BorderSkin SkinStyle="Emboss" /> </Chart> <Chart BackColor="#C9DC87" BackGradientStyle="TopBottom" BorderColor="181, 64, 1" BorderWidth="2" BorderlineDashStyle="Solid" Palette="BrightPastel"> <ChartAreas> <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> <AxisY LineColor="64, 64, 64, 64"> <MajorGrid Interval="Auto" LineColor="64, 64, 64, 64" /> <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold" /> </AxisY> <AxisX LineColor="64, 64, 64, 64"> <MajorGrid LineColor="64, 64, 64, 64" /> <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold" /> </AxisX> <Area3DStyle Inclination="15" IsClustered="False" IsRightAngleAxes="False" Perspective="10" Rotation="10" WallWidth="0" /> </ChartArea> </ChartAreas> <Legends> <Legend _Template_="All" Alignment="Center" BackColor="Transparent" Docking="Bottom" Font="Trebuchet MS, 8.25pt, style=Bold" IsTextAutoFit ="False" LegendStyle="Row"> </Legend> </Legends> <BorderSkin SkinStyle="Emboss" /> </Chart> <Chart Palette="SemiTransparent" BorderColor="#000" BorderWidth="2" BorderlineDashStyle="Solid"> <ChartAreas> <ChartArea _Template_="All" Name="Default"> <AxisX> <MinorGrid Enabled="False" /> <MajorGrid Enabled="False" /> </AxisX> <AxisY> <MajorGrid Enabled="False" /> <MinorGrid Enabled="False" /> </AxisY> </ChartArea> </ChartAreas> </Chart> <Chart BackColor="#555" BackGradientStyle="TopBottom" BorderColor="181, 64, 1" BorderWidth="2" BorderlineDashStyle="Solid" Palette="SemiTransparent" AntiAliasing="All"> <ChartAreas> <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> <Area3DStyle LightStyle="Simplistic" Enable3D="True" Inclination="30" IsClustered="False" IsRightAngleAxes="False" Perspective="10" Rotation="-30" WallWidth="0" /> </ChartArea> </ChartAreas> </Chart> <Chart BackColor="#FADA5E" BackGradientStyle="TopBottom" BorderColor="#B8860B" BorderWidth="2" BorderlineDashStyle="Solid" Palette="EarthTones"> <ChartAreas> <ChartArea Name="Default" _Template_="All" BackColor="Transparent" BackSecondaryColor="White" BorderColor="64, 64, 64, 64" BorderDashStyle="Solid" ShadowColor="Transparent"> <AxisY> <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold" /> </AxisY> <AxisX LineColor="64, 64, 64, 64"> <LabelStyle Font="Trebuchet MS, 8.25pt, style=Bold" /> </AxisX> </ChartArea> </ChartAreas> <Legends> <Legend _Template_="All" Alignment="Left" BackColor="Transparent" Docking="Bottom" Font="Trebuchet MS, 8.25pt, style=Bold" LegendStyle="Row"> </Legend> </Legends> <BorderSkin SkinStyle="Emboss" /> </Chart> ================================================ FILE: src/System.Web.Helpers/Resources/HelpersResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.Helpers.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class HelpersResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal HelpersResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.Helpers.Resources.HelpersResources", typeof(HelpersResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Argument conversion to type "{0}" failed.. /// internal static string Chart_ArgumentConversionFailed { get { return ResourceManager.GetString("Chart_ArgumentConversionFailed", resourceCulture); } } /// /// Looks up a localized string similar to A series cannot be data-bound to a string object.. /// internal static string Chart_ExceptionDataBindSeriesToString { get { return ResourceManager.GetString("Chart_ExceptionDataBindSeriesToString", resourceCulture); } } /// /// Looks up a localized string similar to The theme file "{0}" could not be found.. /// internal static string Chart_ThemeFileNotFound { get { return ResourceManager.GetString("Chart_ThemeFileNotFound", resourceCulture); } } /// /// Looks up a localized string similar to The hash algorithm '{0}' is not supported, valid values are: sha256, sha1, md5. /// internal static string Crypto_NotSupportedHashAlg { get { return ResourceManager.GetString("Crypto_NotSupportedHashAlg", resourceCulture); } } /// /// Looks up a localized string similar to "{0}" is invalid image format. Valid values are image format names like: "JPEG", "BMP", "GIF", "PNG", etc.. /// internal static string Image_IncorrectImageFormat { get { return ResourceManager.GetString("Image_IncorrectImageFormat", resourceCulture); } } /// /// Looks up a localized string similar to Unable to convert to "{0}". Use Json.Decode<T> instead.. /// internal static string Json_UnableToConvertType { get { return ResourceManager.GetString("Json_UnableToConvertType", resourceCulture); } } /// /// Looks up a localized string similar to Previously Displayed. /// internal static string ObjectInfo_PreviousDisplayed { get { return ResourceManager.GetString("ObjectInfo_PreviousDisplayed", resourceCulture); } } /// /// Looks up a localized string similar to Accessing a property threw an exception: . /// internal static string ObjectInfo_PropertyThrewException { get { return ResourceManager.GetString("ObjectInfo_PropertyThrewException", resourceCulture); } } /// /// Looks up a localized string similar to File path "{0}" is invalid.. /// internal static string PathUtils_IncorrectPath { get { return ResourceManager.GetString("PathUtils_IncorrectPath", resourceCulture); } } /// /// Looks up a localized string similar to Additional server information is available when the page is running with high trust.. /// internal static string ServerInfo_AdditionalInfo { get { return ResourceManager.GetString("ServerInfo_AdditionalInfo", resourceCulture); } } /// /// Looks up a localized string similar to Environment Variables. /// internal static string ServerInfo_EnvVars { get { return ResourceManager.GetString("ServerInfo_EnvVars", resourceCulture); } } /// /// Looks up a localized string similar to ASP.NET Server Information. /// internal static string ServerInfo_Header { get { return ResourceManager.GetString("ServerInfo_Header", resourceCulture); } } /// /// Looks up a localized string similar to HTTP Runtime Information. /// internal static string ServerInfo_HttpRuntime { get { return ResourceManager.GetString("ServerInfo_HttpRuntime", resourceCulture); } } /// /// Looks up a localized string similar to Legacy Code Access Security. /// internal static string ServerInfo_LegacyCAS { get { return ResourceManager.GetString("ServerInfo_LegacyCAS", resourceCulture); } } /// /// Looks up a localized string similar to Legacy Code Access Security has been detected on your system. Microsoft WebPage features require the ASP.NET 4 Code Access Security model. For information about how to resolve this, contact your server administrator.. /// internal static string ServerInfo_LegacyCasHelpInfo { get { return ResourceManager.GetString("ServerInfo_LegacyCasHelpInfo", resourceCulture); } } /// /// Looks up a localized string similar to no value. /// internal static string ServerInfo_NoValue { get { return ResourceManager.GetString("ServerInfo_NoValue", resourceCulture); } } /// /// Looks up a localized string similar to Server Configuration. /// internal static string ServerInfo_ServerConfigTable { get { return ResourceManager.GetString("ServerInfo_ServerConfigTable", resourceCulture); } } /// /// Looks up a localized string similar to ASP.NET Server Variables. /// internal static string ServerInfo_ServerVars { get { return ResourceManager.GetString("ServerInfo_ServerVars", resourceCulture); } } /// /// Looks up a localized string similar to The column name cannot be null or an empty string unless a custom format is specified.. /// internal static string WebGrid_ColumnNameOrFormatRequired { get { return ResourceManager.GetString("WebGrid_ColumnNameOrFormatRequired", resourceCulture); } } /// /// Looks up a localized string similar to Column "{0}" does not exist.. /// internal static string WebGrid_ColumnNotFound { get { return ResourceManager.GetString("WebGrid_ColumnNotFound", resourceCulture); } } /// /// Looks up a localized string similar to The WebGrid instance is already bound to a data source.. /// internal static string WebGrid_DataSourceBound { get { return ResourceManager.GetString("WebGrid_DataSourceBound", resourceCulture); } } /// /// Looks up a localized string similar to A data source must be bound before this operation can be performed.. /// internal static string WebGrid_NoDataSourceBound { get { return ResourceManager.GetString("WebGrid_NoDataSourceBound", resourceCulture); } } /// /// Looks up a localized string similar to This operation is not supported when paging is disabled for the "WebGrid" object.. /// internal static string WebGrid_NotSupportedIfPagingIsDisabled { get { return ResourceManager.GetString("WebGrid_NotSupportedIfPagingIsDisabled", resourceCulture); } } /// /// Looks up a localized string similar to This operation is not supported when sorting is disabled for the "WebGrid" object.. /// internal static string WebGrid_NotSupportedIfSortingIsDisabled { get { return ResourceManager.GetString("WebGrid_NotSupportedIfSortingIsDisabled", resourceCulture); } } /// /// Looks up a localized string similar to To use this argument, pager mode "{0}" must be enabled.. /// internal static string WebGrid_PagerModeMustBeEnabled { get { return ResourceManager.GetString("WebGrid_PagerModeMustBeEnabled", resourceCulture); } } /// /// Looks up a localized string similar to This property cannot be set after the "WebGrid" object has been sorted or paged. Make sure that this property is set prior to invoking the "Rows" property directly or indirectly through other methods such as "GetHtml", "Pager", "Table", etc.. /// internal static string WebGrid_PropertySetterNotSupportedAfterDataBound { get { return ResourceManager.GetString("WebGrid_PropertySetterNotSupportedAfterDataBound", resourceCulture); } } /// /// Looks up a localized string similar to A value for "rowCount" must be specified when "autoSortAndPage" is set to true and paging is enabled.. /// internal static string WebGrid_RowCountNotSpecified { get { return ResourceManager.GetString("WebGrid_RowCountNotSpecified", resourceCulture); } } /// /// Looks up a localized string similar to Select. /// internal static string WebGrid_SelectLinkText { get { return ResourceManager.GetString("WebGrid_SelectLinkText", resourceCulture); } } /// /// Looks up a localized string similar to The "fontColor" value is invalid. Valid values are names like "White", "Black", or "DarkBlue", or hexadecimal values in the form "#RRGGBB" or "#RGB".. /// internal static string WebImage_IncorrectColorName { get { return ResourceManager.GetString("WebImage_IncorrectColorName", resourceCulture); } } /// /// Looks up a localized string similar to The "fontFamily" value is invalid. Valid values are font family names like: "Arial", "Times New Roman", etc. Make sure that the font family you are trying to use is installed on the server.. /// internal static string WebImage_IncorrectFontFamily { get { return ResourceManager.GetString("WebImage_IncorrectFontFamily", resourceCulture); } } /// /// Looks up a localized string similar to The "fontStyle" value is invalid. Valid values are: "Regular", "Bold", "Italic", "Underline", and "Strikeout".. /// internal static string WebImage_IncorrectFontStyle { get { return ResourceManager.GetString("WebImage_IncorrectFontStyle", resourceCulture); } } /// /// Looks up a localized string similar to The "horizontalAlign" value is invalid. Valid values are: "Right", "Left", and "Center".. /// internal static string WebImage_IncorrectHorizontalAlignment { get { return ResourceManager.GetString("WebImage_IncorrectHorizontalAlignment", resourceCulture); } } /// /// Looks up a localized string similar to The "verticalAlign" value is invalid. Valid values are: "Top", "Bottom", and "Middle".. /// internal static string WebImage_IncorrectVerticalAlignment { get { return ResourceManager.GetString("WebImage_IncorrectVerticalAlignment", resourceCulture); } } /// /// Looks up a localized string similar to Watermark width and height must both be positive or both be zero.. /// internal static string WebImage_IncorrectWidthAndHeight { get { return ResourceManager.GetString("WebImage_IncorrectWidthAndHeight", resourceCulture); } } /// /// Looks up a localized string similar to An image could not be constructed from the content provided.. /// internal static string WebImage_InvalidImageContents { get { return ResourceManager.GetString("WebImage_InvalidImageContents", resourceCulture); } } /// /// Looks up a localized string similar to The "priority" value is invalid. Valid values are "Low", "Normal" and "High".. /// internal static string WebMail_InvalidPriority { get { return ResourceManager.GetString("WebMail_InvalidPriority", resourceCulture); } } /// /// Looks up a localized string similar to A string in the collection is null or empty.. /// internal static string WebMail_ItemInCollectionIsNull { get { return ResourceManager.GetString("WebMail_ItemInCollectionIsNull", resourceCulture); } } /// /// Looks up a localized string similar to "SmtpServer" was not specified.. /// internal static string WebMail_SmtpServerNotSpecified { get { return ResourceManager.GetString("WebMail_SmtpServerNotSpecified", resourceCulture); } } /// /// Looks up a localized string similar to No "From" email address was specified and a default value could not be assigned.. /// internal static string WebMail_UnableToDetermineFrom { get { return ResourceManager.GetString("WebMail_UnableToDetermineFrom", resourceCulture); } } } } ================================================ FILE: src/System.Web.Helpers/Resources/HelpersResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Argument conversion to type "{0}" failed. A series cannot be data-bound to a string object. The theme file "{0}" could not be found. The hash algorithm '{0}' is not supported, valid values are: sha256, sha1, md5 "{0}" is invalid image format. Valid values are image format names like: "JPEG", "BMP", "GIF", "PNG", etc. Unable to convert to "{0}". Use Json.Decode<T> instead. Previously Displayed Accessing a property threw an exception: File path "{0}" is invalid. Additional server information is available when the page is running with high trust. Environment Variables ASP.NET Server Information HTTP Runtime Information Legacy Code Access Security Legacy Code Access Security has been detected on your system. Microsoft WebPage features require the ASP.NET 4 Code Access Security model. For information about how to resolve this, contact your server administrator. no value Server Configuration ASP.NET Server Variables The column name cannot be null or an empty string unless a custom format is specified. Column "{0}" does not exist. The WebGrid instance is already bound to a data source. A data source must be bound before this operation can be performed. This operation is not supported when paging is disabled for the "WebGrid" object. This operation is not supported when sorting is disabled for the "WebGrid" object. To use this argument, pager mode "{0}" must be enabled. This property cannot be set after the "WebGrid" object has been sorted or paged. Make sure that this property is set prior to invoking the "Rows" property directly or indirectly through other methods such as "GetHtml", "Pager", "Table", etc. A value for "rowCount" must be specified when "autoSortAndPage" is set to true and paging is enabled. Select The "fontColor" value is invalid. Valid values are names like "White", "Black", or "DarkBlue", or hexadecimal values in the form "#RRGGBB" or "#RGB". The "fontFamily" value is invalid. Valid values are font family names like: "Arial", "Times New Roman", etc. Make sure that the font family you are trying to use is installed on the server. The "fontStyle" value is invalid. Valid values are: "Regular", "Bold", "Italic", "Underline", and "Strikeout". The "horizontalAlign" value is invalid. Valid values are: "Right", "Left", and "Center". The "verticalAlign" value is invalid. Valid values are: "Top", "Bottom", and "Middle". Watermark width and height must both be positive or both be zero. An image could not be constructed from the content provided. The "priority" value is invalid. Valid values are "Low", "Normal" and "High". A string in the collection is null or empty. "SmtpServer" was not specified. No "From" email address was specified and a default value could not be assigned. ================================================ FILE: src/System.Web.Helpers/ServerInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Security; using System.Text; using System.Web.Helpers.Resources; using System.Web.WebPages; namespace System.Web.Helpers { /// /// Provides various info about ASP.NET server. /// public static class ServerInfo { /// /// todo: figure out right place for this /// private const string Style = ""; internal static IDictionary EnvironmentVariables() { // todo: extract well defined subset for special use? // use a case-insensitive dictionary since environment variables are case-insensitive. IDictionary environmentVariablesResult = new SortedDictionary(StringComparer.OrdinalIgnoreCase); IDictionary environmentVariables; // todo: better way to deal with security; query config for trust level? try { environmentVariables = Environment.GetEnvironmentVariables(); } catch (SecurityException) { return environmentVariablesResult; } foreach (DictionaryEntry entry in environmentVariables) { environmentVariablesResult.Add(entry.Key.ToString(), InsertWhiteSpace(entry.Value.ToString())); } return environmentVariablesResult; } internal static IDictionary ServerVariables() { var httpContext = HttpContext.Current; return ServerVariables(httpContext != null ? new HttpContextWrapper(httpContext) : null); } internal static IDictionary ServerVariables(HttpContextBase context) { // todo: extract well defined subset for special use? IDictionary serverVariablesResult = new SortedDictionary(); NameValueCollection serverVariables; // todo: better way to deal with security; query config for trust level? try { if ((context != null) && (context.Request != null)) { serverVariables = context.Request.ServerVariables; } else { // Just return empty collection when there is no context available. return serverVariablesResult; } } catch (SecurityException) { return serverVariablesResult; } foreach (string key in serverVariables.AllKeys) { // todo: these values contains very long strings with no spaces that distorts table layout - figure out how to deal with it if (key.Equals("ALL_HTTP", StringComparison.OrdinalIgnoreCase) || key.Equals("ALL_RAW", StringComparison.OrdinalIgnoreCase) || key.Equals("HTTP_AUTHORIZATION", StringComparison.OrdinalIgnoreCase) || key.Equals("HTTP_COOKIE", StringComparison.OrdinalIgnoreCase)) { continue; } serverVariablesResult.Add(key, InsertWhiteSpace(serverVariables[key])); } return serverVariablesResult; } internal static IDictionary Configuration() { IDictionary info = new Dictionary(); // todo: do we need to localize these strings or would that be confusing // (Since we just display API names that are all in English) info.Add("Current Local Time", DateTime.Now.ToString(CultureInfo.CurrentCulture)); info.Add("Current UTC Time", DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)); info.Add("Current Culture", CultureInfo.CurrentCulture.DisplayName); info.Add("Machine Name", Environment.MachineName); info.Add("OS Version", Environment.OSVersion.ToString()); info.Add("ASP.NET Version", Environment.Version.ToString()); info.Add("ASP.NET Web Pages Version", new AssemblyName(typeof(WebPage).Assembly.FullName).Version.ToString()); info.Add("User Name", Environment.UserName); info.Add("User Interactive", Environment.UserInteractive.ToString()); info.Add("Processor Count", Environment.ProcessorCount.ToString(CultureInfo.InvariantCulture)); info.Add("Tick Count", Environment.TickCount.ToString(CultureInfo.InvariantCulture)); // Calls bellow require full trust. try { info.Add("Current Directory", Environment.CurrentDirectory); } catch (SecurityException) { return info; } info.Add("System Directory", Environment.SystemDirectory); info.Add("User Domain Name", Environment.UserDomainName); info.Add("Working Set", Environment.WorkingSet.ToString(CultureInfo.InvariantCulture) + " bytes"); return info; } internal static IDictionary HttpRuntimeInfo() { IDictionary info = new Dictionary(); // todo: better way to deal with security; query config for trust level? try { info.Add("CLR Install Directory", HttpRuntime.ClrInstallDirectory); } catch (SecurityException) { return info; } try { info.Add("Codegen Directory", HttpRuntime.CodegenDir); info.Add("Bin Directory", HttpRuntime.BinDirectory); info.Add("AppDomain Application Path", HttpRuntime.AppDomainAppPath); } catch (ArgumentException) { // do nothing // These APIs don't check if path is set before setting security demands, which causes exception. // So far this happens only when running from unit tests. } info.Add("Asp Install Directory", HttpRuntime.AspInstallDirectory); info.Add("Machine Configuration Directory", HttpRuntime.MachineConfigurationDirectory); info.Add("AppDomain Id", HttpRuntime.AppDomainId); info.Add("AppDomain Application Id", HttpRuntime.AppDomainAppId); info.Add("AppDomain Application Virtual Path", HttpRuntime.AppDomainAppVirtualPath); info.Add("Asp Client Script Physical Path", HttpRuntime.AspClientScriptPhysicalPath); info.Add("Asp Client Script Virtual Path", HttpRuntime.AspClientScriptVirtualPath); info.Add("Cache Size", HttpRuntime.Cache.Count.ToString(CultureInfo.InvariantCulture)); info.Add("Cache Effective Percentage Physical Memory Limit", HttpRuntime.Cache.EffectivePercentagePhysicalMemoryLimit.ToString(CultureInfo.InvariantCulture)); info.Add("Cache Effective Private Bytes Limit", HttpRuntime.Cache.EffectivePrivateBytesLimit.ToString(CultureInfo.InvariantCulture)); info.Add("On UNC Share", HttpRuntime.IsOnUNCShare.ToString()); return info; } internal static IDictionary LegacyCAS() { return LegacyCAS(AppDomain.CurrentDomain); } internal static IDictionary LegacyCAS(AppDomain appDomain) { IDictionary info = new Dictionary(); try { bool legacyCasModeEnabled = !appDomain.IsHomogenous; if (legacyCasModeEnabled) { info[HelpersResources.ServerInfo_LegacyCAS] = HelpersResources.ServerInfo_LegacyCasHelpInfo; } } catch (SecurityException) { return info; } return info; } /// /// Generates HTML required to display server information. /// /// /// HTML generated is XHTML 1.0 compliant but not XHTML 1.1 or HTML5 compliant. The reason is that we /// generate <style> tag inside <body> tag, which is not allowed. This is by design for now since ServerInfo /// is debugging aid and should not be used as a permanent part of any web page. /// [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This could be time consuming operation that does not just retrieve a field.")] public static HtmlString GetHtml() { StringBuilder sb = new StringBuilder(Style); sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "

{0}

", HttpUtility.HtmlEncode(HelpersResources.ServerInfo_Header))); var configuration = Configuration(); Debug.Assert((configuration != null) && (configuration.Count > 0)); PrintInfoSection(sb, HelpersResources.ServerInfo_ServerConfigTable, configuration); var serverVariables = ServerVariables(); Debug.Assert((serverVariables != null)); PrintInfoSection(sb, HelpersResources.ServerInfo_ServerVars, serverVariables); var legacyCAS = LegacyCAS(); if (legacyCAS.Any()) { PrintInfoSection(sb, HelpersResources.ServerInfo_LegacyCAS, legacyCAS); } // Info below is not available in medium trust. var httpRuntimeInfo = HttpRuntimeInfo(); Debug.Assert(httpRuntimeInfo != null); if (!httpRuntimeInfo.Any()) { sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "

{0}

", HttpUtility.HtmlEncode(HelpersResources.ServerInfo_AdditionalInfo))); return new HtmlString(sb.ToString()); } else { PrintInfoSection(sb, HelpersResources.ServerInfo_HttpRuntime, httpRuntimeInfo); var envVariables = EnvironmentVariables(); Debug.Assert(envVariables != null); PrintInfoSection(sb, HelpersResources.ServerInfo_EnvVars, envVariables); } return new HtmlString(sb.ToString()); } /// /// Renders a table section printing out rows and columns. /// private static void PrintInfoSection(StringBuilder builder, string sectionTitle, IDictionary entries) { builder.AppendLine("
"); builder.AppendLine(""); if (!String.IsNullOrEmpty(sectionTitle)) { builder.AppendLine(""); } builder.AppendLine(""); builder.AppendLine(""); foreach (var entry in entries) { var css = String.Empty; string value = entry.Value; if (entry.Key == HelpersResources.ServerInfo_LegacyCAS) { // TODO: suboptimal solution, but its easier to do this than come up with something that works better css = "warn"; } else if (String.IsNullOrEmpty(entry.Value)) { css = "ital"; value = HelpersResources.ServerInfo_NoValue; } if (css.Any()) { css = " class=\"" + css + "\""; } builder.Append(""); builder.AppendFormat(CultureInfo.InvariantCulture, "", HttpUtility.HtmlEncode(entry.Key)); builder.AppendFormat(CultureInfo.InvariantCulture, "{1}", css, HttpUtility.HtmlEncode(value)); builder.AppendLine(""); } builder.AppendLine(""); builder.AppendLine("
"); builder.AppendFormat(CultureInfo.InvariantCulture, "

{0}

", HttpUtility.HtmlEncode(sectionTitle)).AppendLine(); builder.AppendLine("
{0}
"); builder.AppendLine("
"); } /// /// Inserts spaces after ',' and ';' so table can be rendered properly. /// private static string InsertWhiteSpace(string s) { return s.Replace(",", ", ").Replace(";", "; "); } } } ================================================ FILE: src/System.Web.Helpers/SortDirection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Helpers { public enum SortDirection { Ascending, Descending } } ================================================ FILE: src/System.Web.Helpers/System.Web.Helpers.csproj ================================================  {9B7E3740-6161-4548-833C-4BBCA43B970E} Library System.Web.Helpers System.Web.Helpers $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\WebHelpers.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 Properties\CommonAssemblyInfo.cs Common\GlobalSuppressions.cs True True HelpersResources.resx True True _WebGridRenderer.cshtml ResXFileCodeGenerator HelpersResources.Designer.cs {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages RazorHelperGenerator _WebGridRenderer.generated.cs System.Web.Helpers ================================================ FILE: src/System.Web.Helpers/WebCache.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Runtime.Caching; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { public static class WebCache { public static void Set(string key, object value, int minutesToCache = 20, bool slidingExpiration = true) { if (minutesToCache <= 0) { throw new ArgumentOutOfRangeException("minutesToCache", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThan, 0)); } else if (slidingExpiration && (minutesToCache > 365 * 24 * 60)) { // For sliding expiration policies, MemoryCache has a time limit of 365 days. throw new ArgumentOutOfRangeException("minutesToCache", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_LessThanOrEqualTo, 365 * 24 * 60)); } CacheItemPolicy policy = new CacheItemPolicy(); TimeSpan expireTime = new TimeSpan(0, minutesToCache, 0); if (slidingExpiration) { policy.SlidingExpiration = expireTime; } else { policy.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutesToCache); } MemoryCache.Default.Set(key, value, policy); } public static dynamic Get(string key) { return MemoryCache.Default.Get(key); } public static dynamic Remove(string key) { return MemoryCache.Default.Remove(key); } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/IWebGridDataSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace System.Web.Helpers { internal interface IWebGridDataSource { int TotalRowCount { get; } IList GetRows(SortInfo sortInfo, int pageIndex); } } ================================================ FILE: src/System.Web.Helpers/WebGrid/PreComputedGridDataSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace System.Web.Helpers { /// /// Source wrapper for data provided by the user that is already sorted and paged. The user provides the WebGrid the rows to bind and additionally the total number of rows that /// are available. /// internal sealed class PreComputedGridDataSource : IWebGridDataSource { private readonly int _totalRows; private readonly IList _rows; public PreComputedGridDataSource(WebGrid grid, IEnumerable values, int totalRows) { Debug.Assert(grid != null); Debug.Assert(values != null); _totalRows = totalRows; _rows = values.Select((value, index) => new WebGridRow(grid, value: value, rowIndex: index)).ToList(); } public int TotalRowCount { get { return _totalRows; } } public IList GetRows(SortInfo sortInfo, int pageIndex) { // Data is already sorted and paged. Ignore parameters. return _rows; } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/SortInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Helpers { internal sealed class SortInfo : IEquatable { public string SortColumn { get; set; } public SortDirection SortDirection { get; set; } public bool Equals(SortInfo other) { return other != null && String.Equals(SortColumn, other.SortColumn, StringComparison.OrdinalIgnoreCase) && SortDirection == other.SortDirection; } public override bool Equals(object obj) { SortInfo sortInfo = obj as SortInfo; if (sortInfo != null) { return Equals(sortInfo); } return base.Equals(obj); } public override int GetHashCode() { return SortColumn.GetHashCode(); } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/WebGrid.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Web.Helpers.Resources; using System.Web.WebPages; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { public class WebGrid { private const string AjaxUpdateScript = "$({1}).swhgLoad({0},{1}{2});"; private readonly HttpContextBase _context; private readonly bool _canPage; private readonly bool _canSort; private readonly string _ajaxUpdateContainerId; private readonly string _ajaxUpdateCallback; private readonly string _defaultSort; private readonly string _pageFieldName = "page"; private readonly string _sortDirectionFieldName = "sortdir"; private readonly string _selectionFieldName = "row"; private readonly string _sortFieldName = "sort"; private readonly string _fieldNamePrefix; private int _pageIndex = -1; private bool _pageIndexSet; private int _rowsPerPage; private int _selectedIndex = -1; private bool _selectedIndexSet; private string _sortColumn; private bool _sortColumnSet; private bool _sortColumnExplicitlySet; private SortDirection _sortDirection; private bool _sortDirectionSet; private IWebGridDataSource _dataSource; private bool _dataSourceBound; private bool _dataSourceMaterialized; private IEnumerable _columnNames; private Type _elementType; private IList _rows; /// Data source /// Data source column names. Auto-populated by default. /// Default sort column. /// Number of rows per page. /// /// /// ID for the grid's container element. This enables AJAX support. /// Callback function for the AJAX functionality once the update is complete /// Prefix for query string fields to support multiple grids. /// Query string field name for page number. /// Query string field name for selected row number. /// Query string field name for sort column. /// Query string field name for sort direction. #if CODE_COVERAGE [ExcludeFromCodeCoverage] #endif public WebGrid( IEnumerable source = null, IEnumerable columnNames = null, string defaultSort = null, int rowsPerPage = 10, bool canPage = true, bool canSort = true, string ajaxUpdateContainerId = null, string ajaxUpdateCallback = null, string fieldNamePrefix = null, string pageFieldName = null, string selectionFieldName = null, string sortFieldName = null, string sortDirectionFieldName = null) : this(new HttpContextWrapper(Web.HttpContext.Current), defaultSort: defaultSort, rowsPerPage: rowsPerPage, canPage: canPage, canSort: canSort, ajaxUpdateContainerId: ajaxUpdateContainerId, ajaxUpdateCallback: ajaxUpdateCallback, fieldNamePrefix: fieldNamePrefix, pageFieldName: pageFieldName, selectionFieldName: selectionFieldName, sortFieldName: sortFieldName, sortDirectionFieldName: sortDirectionFieldName) { if (source != null) { Bind(source, columnNames); } } // NOTE: WebGrid uses an IEnumerable data source instead of IEnumerable to avoid generics in the syntax. internal WebGrid( HttpContextBase context, string defaultSort = null, int rowsPerPage = 10, bool canPage = true, bool canSort = true, string ajaxUpdateContainerId = null, string ajaxUpdateCallback = null, string fieldNamePrefix = null, string pageFieldName = null, string selectionFieldName = null, string sortFieldName = null, string sortDirectionFieldName = null) { Debug.Assert(context != null); if (rowsPerPage < 1) { throw new ArgumentOutOfRangeException("rowsPerPage", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 1)); } _context = context; _defaultSort = defaultSort; _rowsPerPage = rowsPerPage; _canPage = canPage; _canSort = canSort; _ajaxUpdateContainerId = ajaxUpdateContainerId; _ajaxUpdateCallback = ajaxUpdateCallback; _fieldNamePrefix = fieldNamePrefix; if (!String.IsNullOrEmpty(pageFieldName)) { _pageFieldName = pageFieldName; } if (!String.IsNullOrEmpty(selectionFieldName)) { _selectionFieldName = selectionFieldName; } if (!String.IsNullOrEmpty(sortFieldName)) { _sortFieldName = sortFieldName; } if (!String.IsNullOrEmpty(sortDirectionFieldName)) { _sortDirectionFieldName = sortDirectionFieldName; } CustomSorters = new Dictionary(StringComparer.OrdinalIgnoreCase); } public IEnumerable ColumnNames { get { // Review: Assuming that the users always binds the source and provides column names / we infer the default columns names on binding // Would not work if we want to allow column names to be independently set. EnsureDataBound(); return _columnNames; } } public bool CanSort { get { return _canSort; } } public string AjaxUpdateContainerId { get { return _ajaxUpdateContainerId; } } public bool IsAjaxEnabled { get { return !String.IsNullOrEmpty(_ajaxUpdateContainerId); } } public string AjaxUpdateCallback { get { return _ajaxUpdateCallback; } } public string FieldNamePrefix { get { return _fieldNamePrefix ?? String.Empty; } } public bool HasSelection { get { return SelectedIndex >= 0; } } public int PageCount { get { if (!_canPage) { return 1; } return (int)Math.Ceiling((double)TotalRowCount / RowsPerPage); } } public string PageFieldName { get { return FieldNamePrefix + _pageFieldName; } } public int PageIndex { get { if (!_canPage) { //Default page index is 0 return 0; } if (!_pageIndexSet) { int page; if (!_canPage || !Int32.TryParse(QueryString[PageFieldName], out page) || (page < 1)) { page = 1; } if (_dataSourceBound && page > PageCount) { page = PageCount; } _pageIndex = page - 1; _pageIndexSet = true; } return _pageIndex; } set { if (!_canPage) { throw new NotSupportedException(HelpersResources.WebGrid_NotSupportedIfPagingIsDisabled); } if (!_dataSourceBound) { // Allow the user to specify arbitrary non-negative values before data binding if (value < 0) { throw new ArgumentOutOfRangeException("value", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 0)); } else { _pageIndex = value; _pageIndexSet = true; } } else { // Once data bound, perform bounds check on the PageIndex. Also ensure the data source has not been materialized. if ((value < 0) || (value >= PageCount)) { throw new ArgumentOutOfRangeException("value", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_Between, 0, (PageCount - 1))); } else if (value != _pageIndex) { EnsureDataSourceNotMaterialized(); _pageIndex = value; _pageIndexSet = true; } } } } public IList Rows { get { EnsureDataBound(); if (!_dataSourceMaterialized) { _rows = _dataSource.GetRows(SortInfo, PageIndex); _dataSourceMaterialized = true; } return _rows; } } public int RowsPerPage { get { return _rowsPerPage; } } public WebGridRow SelectedRow { get { if ((SelectedIndex >= 0) && (SelectedIndex < Rows.Count)) { return Rows[SelectedIndex]; } return null; } } public int SelectedIndex { get { if (!_selectedIndexSet) { int row; // Range checking should not use Rows.Count since this will cause paging and sorting. // Review: side effect is that HasSelection will return true if Rows.Count (current page's // row count) is less than both SelectedIndex and RowsPerPage. This scenario should only // happen if someone manually modifies the query string. // If paging isn't enabled, this getter isn't doing a upper bounds check on the value. if ((!Int32.TryParse(QueryString[SelectionFieldName], out row)) || (row < 1) || (_canPage && (row > RowsPerPage))) { row = 0; } _selectedIndex = row - 1; _selectedIndexSet = true; } return _selectedIndex; } set { if (_selectedIndex != value) { EnsureDataSourceNotMaterialized(); _selectedIndex = value; } _selectedIndexSet = true; } } public string SelectionFieldName { get { return FieldNamePrefix + _selectionFieldName; } } public string SortColumn { get { if (!_sortColumnSet) { string sortColumn = QueryString[SortFieldName]; if (!_dataSourceBound || ValidateSortColumn(sortColumn)) { _sortColumn = sortColumn; _sortColumnSet = true; } } if (String.IsNullOrEmpty(_sortColumn)) { return _defaultSort ?? String.Empty; } return _sortColumn; } set { EnsureDataBound(); if (!SortColumn.Equals(value, StringComparison.OrdinalIgnoreCase)) { EnsureDataSourceNotMaterialized(); _sortColumn = value; } _sortColumnSet = true; _sortColumnExplicitlySet = true; } } public SortDirection SortDirection { get { if (!_sortDirectionSet) { string sortDirection = QueryString[SortDirectionFieldName]; if (sortDirection != null) { if (sortDirection.Equals("DESC", StringComparison.OrdinalIgnoreCase) || sortDirection.Equals("DESCENDING", StringComparison.OrdinalIgnoreCase)) { _sortDirection = SortDirection.Descending; } } _sortDirectionSet = true; } return _sortDirection; } set { if (!_dataSourceBound) { _sortDirection = value; } else if (_sortDirection != value) { EnsureDataSourceNotMaterialized(); _sortDirection = value; } _sortDirectionSet = true; } } private SortInfo SortInfo { get { return new SortInfo { SortColumn = SortColumn, SortDirection = SortDirection }; } } public string SortDirectionFieldName { get { return FieldNamePrefix + _sortDirectionFieldName; } } public string SortFieldName { get { return FieldNamePrefix + _sortFieldName; } } internal IDictionary CustomSorters { get; private set; } public int TotalRowCount { get { EnsureDataBound(); return _dataSource.TotalRowCount; } } private HttpContextBase HttpContext { get { return _context; } } private NameValueCollection QueryString { get { return HttpContext.Request.QueryString; } } internal static Type GetElementType(IEnumerable source) { Debug.Assert(source != null, "source cannot be null"); Type sourceType = source.GetType(); if (source.FirstOrDefault() is IDynamicMetaObjectProvider) { return typeof(IDynamicMetaObjectProvider); } else if (sourceType.IsArray) { return sourceType.GetElementType(); } Type elementType = sourceType.GetInterfaces().Select(GetGenericEnumerableType).FirstOrDefault(t => t != null); Debug.Assert(elementType != null); return elementType; } private static Type GetGenericEnumerableType(Type type) { Type enumerableType = typeof(IEnumerable<>); if (type.IsGenericType && enumerableType.IsAssignableFrom(type.GetGenericTypeDefinition())) { return type.GetGenericArguments()[0]; } return null; } public WebGrid Bind(IEnumerable source, IEnumerable columnNames = null, bool autoSortAndPage = true, int rowCount = -1) { if (_dataSourceBound) { throw new InvalidOperationException(HelpersResources.WebGrid_DataSourceBound); } if (source == null) { throw new ArgumentNullException("source"); } if (!autoSortAndPage && _canPage && rowCount == -1) { throw new ArgumentException(HelpersResources.WebGrid_RowCountNotSpecified, "rowCount"); } _elementType = GetElementType(source); if (_columnNames == null) { _columnNames = columnNames ?? GetDefaultColumnNames(source, elementType: _elementType); } if (!autoSortAndPage) { _dataSource = new PreComputedGridDataSource(grid: this, values: source, totalRows: rowCount); } else { WebGridDataSource dataSource = new WebGridDataSource(grid: this, values: source, elementType: _elementType, canPage: _canPage, canSort: _canSort); dataSource.DefaultSort = new SortInfo { SortColumn = _defaultSort, SortDirection = SortDirection.Ascending }; dataSource.RowsPerPage = _rowsPerPage; _dataSource = dataSource; } _dataSourceBound = true; ValidatePreDataBoundValues(); return this; } // todo: add templating from file support [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Non-static for syntax, and in case we want to check column existence.")] public WebGridColumn Column(string columnName = null, string header = null, Func format = null, string style = null, bool canSort = true) { if (String.IsNullOrEmpty(columnName)) { if (format == null) { throw new ArgumentException(HelpersResources.WebGrid_ColumnNameOrFormatRequired, "columnName"); } } return new WebGridColumn { ColumnName = columnName, Header = header, Format = format, Style = style, CanSort = canSort }; } // Should we keep this no-op API for improved WebGrid syntax? Alternatives are: // 1. columns: grid.Columns( // grid.Column(...), grid.Column(...) // ) // 2. columns: new[] { // grid.Column(...), grid.Column(...) // } [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Review: No-op API for syntax simplification?")] public WebGridColumn[] Columns(params WebGridColumn[] columnSet) { return columnSet; } public IHtmlString GetContainerUpdateScript(string path) { var script = String.Format(CultureInfo.InvariantCulture, AjaxUpdateScript, HttpUtility.JavaScriptStringEncode(path, addDoubleQuotes: true), HttpUtility.JavaScriptStringEncode('#' + AjaxUpdateContainerId, addDoubleQuotes: true), !String.IsNullOrEmpty(AjaxUpdateCallback) ? ',' + HttpUtility.JavaScriptStringEncode(AjaxUpdateCallback) : String.Empty); return new HtmlString(HttpUtility.HtmlAttributeEncode(script)); } /// /// Gets the HTML for a table with a pager. /// /// Table class for styling. /// Header row class for styling. /// Footer row class for styling. /// Row class for styling (odd rows only). /// Row class for styling (even rows only). /// Selected row class for styling. /// Whether the header row should be displayed. /// The string displayed as the table caption /// Whether the table can add empty rows to ensure the rowsPerPage row count. /// Value used to populate empty rows. This property is only used when is set /// Column model for customizing column rendering. /// Columns to exclude when auto-populating columns. /// Modes for pager rendering. /// Text for link to first page. /// Text for link to previous page. /// Text for link to next page. /// Text for link to last page. /// Number of numeric links that should display. /// An object that contains the HTML attributes to set for the element. public IHtmlString GetHtml( string tableStyle = null, string headerStyle = null, string footerStyle = null, string rowStyle = null, string alternatingRowStyle = null, string selectedRowStyle = null, string caption = null, bool displayHeader = true, bool fillEmptyRows = false, string emptyRowCellValue = null, IEnumerable columns = null, IEnumerable exclusions = null, WebGridPagerModes mode = WebGridPagerModes.NextPrevious | WebGridPagerModes.Numeric, string firstText = null, string previousText = null, string nextText = null, string lastText = null, int numericLinksCount = 5, object htmlAttributes = null) { Func footer = null; if (_canPage && (PageCount > 1)) { footer = item => Pager(mode, firstText, previousText, nextText, lastText, numericLinksCount, explicitlyCalled: false); } return Table(tableStyle, headerStyle, footerStyle, rowStyle, alternatingRowStyle, selectedRowStyle, caption, displayHeader, fillEmptyRows, emptyRowCellValue, columns, exclusions, footer: footer, htmlAttributes: htmlAttributes); } [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Strings are easier for Plan9 developer to work with")] public string GetPageUrl(int pageIndex) { if (!_canPage) { throw new NotSupportedException(HelpersResources.WebGrid_NotSupportedIfPagingIsDisabled); } if ((pageIndex < 0) || (pageIndex >= PageCount)) { throw new ArgumentOutOfRangeException("pageIndex", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_Between, 0, (PageCount - 1))); } NameValueCollection queryString = new NameValueCollection(1); queryString[PageFieldName] = (pageIndex + 1L).ToString(CultureInfo.CurrentCulture); return GetPath(queryString, SelectionFieldName); } [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Strings are easier for Plan9 developer to work with")] public string GetSortUrl(string column) { if (!_canSort) { throw new NotSupportedException(HelpersResources.WebGrid_NotSupportedIfSortingIsDisabled); } if (String.IsNullOrEmpty(column)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "column"); } var sort = SortColumn; var sortDir = SortDirection.Ascending; if (column.Equals(sort, StringComparison.OrdinalIgnoreCase)) { if (SortDirection == SortDirection.Ascending) { sortDir = SortDirection.Descending; } } NameValueCollection queryString = new NameValueCollection(2); queryString[SortFieldName] = column; queryString[SortDirectionFieldName] = GetSortDirectionString(sortDir); return GetPath(queryString, PageFieldName, SelectionFieldName); } /// /// Gets the HTML for a pager. /// /// Modes for pager rendering. /// Text for link to first page. /// Text for link to previous page. /// Text for link to next page. /// Text for link to last page. /// Number of numeric links that should display. public HelperResult Pager( WebGridPagerModes mode = WebGridPagerModes.NextPrevious | WebGridPagerModes.Numeric, string firstText = null, string previousText = null, string nextText = null, string lastText = null, int numericLinksCount = 5) { return Pager(mode, firstText, previousText, nextText, lastText, numericLinksCount, explicitlyCalled: true); } /// Modes for pager rendering. /// Text for link to first page. /// Text for link to previous page. /// Text for link to next page. /// Text for link to last page. /// Number of numeric links that should display. /// The Pager can be explicitly called by the public API or is called by the WebGrid when no footer is provided. /// In the explicit scenario, we would need to render a container for the pager to allow identifying the pager links. /// In the implicit scenario, the grid table would be the container. /// private HelperResult Pager( WebGridPagerModes mode, string firstText, string previousText, string nextText, string lastText, int numericLinksCount, bool explicitlyCalled) { if (!_canPage) { throw new NotSupportedException(HelpersResources.WebGrid_NotSupportedIfPagingIsDisabled); } if (!ModeEnabled(mode, WebGridPagerModes.FirstLast) && (firstText != null)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_PagerModeMustBeEnabled, "FirstLast"), "firstText"); } if (!ModeEnabled(mode, WebGridPagerModes.NextPrevious) && (previousText != null)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_PagerModeMustBeEnabled, "NextPrevious"), "previousText"); } if (!ModeEnabled(mode, WebGridPagerModes.NextPrevious) && (nextText != null)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_PagerModeMustBeEnabled, "NextPrevious"), "nextText"); } if (!ModeEnabled(mode, WebGridPagerModes.FirstLast) && (lastText != null)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_PagerModeMustBeEnabled, "FirstLast"), "lastText"); } if (numericLinksCount < 0) { throw new ArgumentOutOfRangeException("numericLinksCount", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, 0)); } return WebGridRenderer.Pager(this, HttpContext, mode: mode, firstText: firstText, previousText: previousText, nextText: nextText, lastText: lastText, numericLinksCount: numericLinksCount, renderAjaxContainer: explicitlyCalled); } /// /// Gets the HTML for a table with a pager. /// /// Table class for styling. /// Header row class for styling. /// Footer row class for styling. /// Row class for styling (odd rows only). /// Row class for styling (even rows only). /// Selected row class for styling. /// The table caption /// Whether the header row should be displayed. /// Whether the table can add empty rows to ensure the rowsPerPage row count. /// Value used to populate empty rows. This property is only used when is set /// Column model for customizing column rendering. /// Columns to exclude when auto-populating columns. /// Table footer template. /// An object that contains the HTML attributes to set for the element. public IHtmlString Table( string tableStyle = null, string headerStyle = null, string footerStyle = null, string rowStyle = null, string alternatingRowStyle = null, string selectedRowStyle = null, string caption = null, bool displayHeader = true, bool fillEmptyRows = false, string emptyRowCellValue = null, IEnumerable columns = null, IEnumerable exclusions = null, Func footer = null, object htmlAttributes = null) { if (columns == null) { columns = GetDefaultColumns(exclusions); } // In order of precedence, the parameters that affect the visibility of columns in WebGrid - // (1) "columns" argument of this method // (2) "exclusion" argument of this method // (3) "columnNames" argument of the constructor. // At the time of binding we can verify if a simple property specified in the query string is a column that would be visible to the user. // However, for complex properties or if either of (1) or (2) arguments are specified, we can only verify at this point. EnsureColumnIsSortable(columns); if (emptyRowCellValue == null) { emptyRowCellValue = " "; } return WebGridRenderer.Table(this, HttpContext, tableStyle: tableStyle, headerStyle: headerStyle, footerStyle: footerStyle, rowStyle: rowStyle, alternatingRowStyle: alternatingRowStyle, selectedRowStyle: selectedRowStyle, caption: caption, displayHeader: displayHeader, fillEmptyRows: fillEmptyRows, emptyRowCellValue: emptyRowCellValue, columns: columns, exclusions: exclusions, footer: footer, htmlAttributes: htmlAttributes); } /// /// Adds a specific sort function for a given column. /// /// The type of elements in the grid's source. /// The column type, usually inferred from the keySelector function's return type. /// The column name (as used for sorting) /// The function used to select a key to sort by, for each element in the grid's source. /// The current grid, with the new custom sorter applied. /// /// /// var grid = new WebGrid(items) /// .AddSorter("Manager.Name", (Employee x) => (x == null || x.Manager == null) ? null : x.Manager.Name); /// /// [SuppressMessage("Microsoft.Design", "CA1006", Justification = "This design make sense, and is a reasonable user experience for users of the helpers")] public WebGrid AddSorter(string columnName, Expression> keySelector) { CustomSorters[columnName] = keySelector; return this; } /// The set of columns that are rendered to the client. private void EnsureColumnIsSortable(IEnumerable columns) { // Fix for bug 941102 // The ValidateSortColumn can validate a few regular cases for sorting and reset those values to default. However, for sort columns that are complex expressions, // or if the user specifies a subset of columns in the GetHtml method (via columns / exclusions), the method is ineffective. // Review: Should this method not throw if the data was not explicitly sorted and paged by the user if (_canSort && !_sortColumnExplicitlySet && !String.IsNullOrEmpty(SortColumn) && !StringComparer.OrdinalIgnoreCase.Equals(_defaultSort, SortColumn) && !columns.Select(c => c.ColumnName).Contains(SortColumn, StringComparer.OrdinalIgnoreCase)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_ColumnNotFound, SortColumn)); } } internal static dynamic GetMember(WebGridRow row, string name) { object result; if (row.TryGetMember(name, out result)) { return result; } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_ColumnNotFound, name)); } // review: make sure this is ordered internal string GetPath(NameValueCollection queryString, params string[] exclusions) { NameValueCollection temp = new NameValueCollection(QueryString); // update current query string in case values were set programmatically if (temp.AllKeys.Contains(PageFieldName)) { temp.Set(PageFieldName, (PageIndex + 1L).ToString(CultureInfo.CurrentCulture)); } if (temp.AllKeys.Contains(SelectionFieldName)) { if (SelectedIndex < 0) { temp.Remove(SelectionFieldName); } else { temp.Set(SelectionFieldName, (SelectedIndex + 1L).ToString(CultureInfo.CurrentCulture)); } } if (temp.AllKeys.Contains(SortFieldName)) { if (String.IsNullOrEmpty(SortColumn)) { temp.Remove(SortFieldName); } else { temp.Set(SortFieldName, SortColumn); } } if (temp.AllKeys.Contains(SortDirectionFieldName)) { temp.Set(SortDirectionFieldName, GetSortDirectionString(SortDirection)); } // remove fields from exclusions list foreach (var key in exclusions) { temp.Remove(key); } // replace with new field values foreach (string key in queryString.Keys) { temp.Set(key, queryString[key]); } queryString = temp; StringBuilder sb = new StringBuilder(HttpContext.Request.Path); sb.Append("?"); for (int i = 0; i < queryString.Count; i++) { if (i > 0) { sb.Append("&"); } sb.Append(HttpUtility.UrlEncode(queryString.Keys[i])); sb.Append("="); sb.Append(HttpUtility.UrlEncode(queryString[i])); } return sb.ToString(); } internal static string GetSortDirectionString(SortDirection sortDir) { return (sortDir == SortDirection.Ascending) ? "ASC" : "DESC"; } private void EnsureDataBound() { if (!_dataSourceBound) { throw new InvalidOperationException(HelpersResources.WebGrid_NoDataSourceBound); } } private void EnsureDataSourceNotMaterialized() { if (_dataSourceMaterialized) { throw new InvalidOperationException(HelpersResources.WebGrid_PropertySetterNotSupportedAfterDataBound); } } private void ValidatePreDataBoundValues() { if (_canPage && _pageIndexSet && PageIndex > PageCount) { PageIndex = PageCount; } else if (_canSort && _sortColumnSet && !ValidateSortColumn(SortColumn)) { SortColumn = _defaultSort; } } private bool ValidateSortColumn(string value) { Debug.Assert(ColumnNames != null); // Navigation columns that contain '.' will be validated during the Sort operation // Validate other properties up-front and ignore any bad columns passed via the query string return _sortColumnExplicitlySet || String.IsNullOrEmpty(value) || StringComparer.OrdinalIgnoreCase.Equals(_defaultSort, value) || ColumnNames.Contains(value, StringComparer.OrdinalIgnoreCase) || value.Contains('.'); } private static IEnumerable GetDefaultColumnNames(IEnumerable source, Type elementType) { var dynObj = source.FirstOrDefault() as IDynamicMetaObjectProvider; if (dynObj != null) { return DynamicHelper.GetMemberNames(dynObj); } else { return (from p in elementType.GetProperties() where IsBindableType(p.PropertyType) && (p.GetIndexParameters().Length == 0) select p.Name).OrderBy(n => n, StringComparer.OrdinalIgnoreCase).ToArray(); } } private IEnumerable GetDefaultColumns(IEnumerable exclusions) { IEnumerable names = ColumnNames; if (exclusions != null) { names = names.Except(exclusions); } return (from n in names select new WebGridColumn { ColumnName = n, CanSort = true }).ToArray(); } // see: DataBoundControlHelper.IsBindableType private static bool IsBindableType(Type type) { Debug.Assert(type != null); Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { type = underlyingType; } return (type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)) || type.Equals(typeof(Decimal)) || type.Equals(typeof(Guid)) || type.Equals(typeof(DateTimeOffset)) || type.Equals(typeof(TimeSpan))); } private static bool ModeEnabled(WebGridPagerModes mode, WebGridPagerModes modeCheck) { return (mode & modeCheck) == modeCheck; } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/WebGridColumn.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Helpers { public class WebGridColumn { public bool CanSort { get; set; } public string ColumnName { get; set; } public Func Format { get; set; } public string Header { get; set; } public string Style { get; set; } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/WebGridDataSource.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Microsoft.CSharp.RuntimeBinder; using Binder = Microsoft.CSharp.RuntimeBinder.Binder; namespace System.Web.Helpers { /// /// Default data source that sorts results if a sort column is specified. /// internal sealed class WebGridDataSource : IWebGridDataSource { private static readonly MethodInfo SortGenericExpressionMethod = typeof(WebGridDataSource).GetMethod("SortGenericExpression", BindingFlags.Static | BindingFlags.NonPublic); private readonly WebGrid _grid; private readonly Type _elementType; private readonly IEnumerable _values; private readonly bool _canPage; private readonly bool _canSort; public WebGridDataSource(WebGrid grid, IEnumerable values, Type elementType, bool canPage, bool canSort) { Debug.Assert(grid != null); Debug.Assert(values != null); _grid = grid; _values = values; _elementType = elementType; _canPage = canPage; _canSort = canSort; } public SortInfo DefaultSort { get; set; } public int RowsPerPage { get; set; } public int TotalRowCount { get { return _values.Count(); } } public IList GetRows(SortInfo sortInfo, int pageIndex) { IEnumerable rowData = _values; if (_canSort) { rowData = Sort(_values.AsQueryable(), sortInfo); } rowData = Page(rowData, pageIndex); try { // Force compile the underlying IQueryable rowData = rowData.ToList(); } catch (ArgumentException) { // The OrderBy method uses a generic comparer which fails when the collection contains 2 or more // items that cannot be compared (e.g. DBNulls, mixed types such as strings and ints et al) with the exception // System.ArgumentException: At least one object must implement IComparable. // Silently fail if this exception occurs and declare that the two items are equivalent rowData = Page(_values.AsQueryable(), pageIndex); } return rowData.Select((value, index) => new WebGridRow(_grid, value: value, rowIndex: index)).ToList(); } private IQueryable Sort(IQueryable data, SortInfo sortInfo) { if (!String.IsNullOrEmpty(sortInfo.SortColumn) || ((DefaultSort != null) && !String.IsNullOrEmpty(DefaultSort.SortColumn))) { return Sort(data, _elementType, sortInfo); } return data; } private IEnumerable Page(IEnumerable data, int pageIndex) { if (_canPage) { Debug.Assert(RowsPerPage > 0); return data.Skip(pageIndex * RowsPerPage).Take(RowsPerPage); } return data; } private IQueryable Sort(IQueryable data, Type elementType, SortInfo sort) { Debug.Assert(data != null); if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(elementType)) { // IDynamicMetaObjectProvider properties are only available through a runtime binder, so we // must build a custom LINQ expression for getting the dynamic property value. // Lambda: o => o.Property (where Property is obtained by runtime binder) // NOTE: lambda must not use internals otherwise this will fail in partial trust when Helpers assembly is in GAC var binder = Binder.GetMember(CSharpBinderFlags.None, sort.SortColumn, typeof(WebGrid), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var param = Expression.Parameter(typeof(IDynamicMetaObjectProvider), "o"); var getter = Expression.Dynamic(binder, typeof(object), param); return SortGenericExpression(data, getter, param, sort.SortDirection); } Expression sorterFunctionBody; ParameterExpression sorterFunctionParameter; Expression sorter; if (_grid.CustomSorters.TryGetValue(sort.SortColumn, out sorter)) { var lambda = sorter as LambdaExpression; Debug.Assert(lambda != null); sorterFunctionBody = lambda.Body; sorterFunctionParameter = lambda.Parameters[0]; } else { // The IQueryable data source is cast as IQueryable at runtime. We must call // SortGenericExpression using reflection so that the LINQ expressions use the actual element type. // Lambda: o => o.Property[.NavigationProperty,etc] sorterFunctionParameter = Expression.Parameter(elementType, "o"); Expression member = sorterFunctionParameter; var type = elementType; var sorts = sort.SortColumn.Split('.'); foreach (var name in sorts) { PropertyInfo prop = type.GetProperty(name); if (prop == null) { // no-op in case navigation property came from querystring (falls back to default sort) if ((DefaultSort != null) && !sort.Equals(DefaultSort) && !String.IsNullOrEmpty(DefaultSort.SortColumn)) { return Sort(data, elementType, DefaultSort); } return data; } member = Expression.Property(member, prop); type = prop.PropertyType; } sorterFunctionBody = member; } var actualSortMethod = SortGenericExpressionMethod.MakeGenericMethod(elementType, sorterFunctionBody.Type); return (IQueryable)actualSortMethod.Invoke(null, new object[] { data, sorterFunctionBody, sorterFunctionParameter, sort.SortDirection }); } private static IQueryable SortGenericExpression(IQueryable data, Expression body, ParameterExpression param, SortDirection sortDirection) { Debug.Assert(data != null); Debug.Assert(body != null); Debug.Assert(param != null); // The IQueryable data source is cast as an IQueryable at runtime. We must cast // this to an IQueryable so that the reflection done by the LINQ expressions will work. IQueryable data2 = data.Cast(); Expression> lambda = Expression.Lambda>(body, param); if (sortDirection == SortDirection.Descending) { return data2.OrderByDescending(lambda); } else { return data2.OrderBy(lambda); } } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/WebGridPagerModes.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Helpers { [Flags] public enum WebGridPagerModes { Numeric = 0x1, NextPrevious = 0x2, FirstLast = 0x4, All = 0x7 } } ================================================ FILE: src/System.Web.Helpers/WebGrid/WebGridRow.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Helpers.Resources; using Microsoft.Internal.Web.Utils; namespace System.Web.Helpers { [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Collection is not an appropriate suffix for this class")] public class WebGridRow : DynamicObject, IEnumerable { private const string RowIndexMemberName = "ROW"; private const BindingFlags BindFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase; private WebGrid _grid; private IDynamicMetaObjectProvider _dynamic; private int _rowIndex; private object _value; private IEnumerable _values; public WebGridRow(WebGrid webGrid, object value, int rowIndex) { _grid = webGrid; _value = value; _rowIndex = rowIndex; _dynamic = value as IDynamicMetaObjectProvider; } public dynamic Value { get { return _value; } } public WebGrid WebGrid { get { return _grid; } } public object this[string name] { get { if (String.IsNullOrEmpty(name)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "name"); } object value = null; if (!TryGetMember(name, out value)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, HelpersResources.WebGrid_ColumnNotFound, name)); } return value; } } public object this[int index] { get { if ((index < 0) || (index >= _grid.ColumnNames.Count())) { throw new ArgumentOutOfRangeException("index"); } return this.Skip(index).First(); } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator GetEnumerator() { if (_values == null) { _values = _grid.ColumnNames.Select(c => WebGrid.GetMember(this, c)); } return _values.GetEnumerator(); } public IHtmlString GetSelectLink(string text = null) { if (String.IsNullOrEmpty(text)) { text = HelpersResources.WebGrid_SelectLinkText; } return WebGridRenderer.GridLink(_grid, GetSelectUrl(), text); } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "review: I think a method is more appropriate here")] [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Strings are easier for Plan9 developer to work with")] public string GetSelectUrl() { NameValueCollection queryString = new NameValueCollection(1); queryString[WebGrid.SelectionFieldName] = (_rowIndex + 1L).ToString(CultureInfo.CurrentCulture); return WebGrid.GetPath(queryString); } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; // Try to get the row index if (TryGetRowIndex(binder.Name, out result)) { return true; } // Try to evaluate the dynamic member based on the binder if (_dynamic != null && DynamicHelper.TryGetMemberValue(_dynamic, binder, out result)) { return true; } return TryGetComplexMember(_value, binder.Name, out result); } internal bool TryGetMember(string memberName, out object result) { result = null; // Try to get the row index if (TryGetRowIndex(memberName, out result)) { return true; } // Try to evaluate the dynamic member based on the name if (_dynamic != null && DynamicHelper.TryGetMemberValue(_dynamic, memberName, out result)) { return true; } // Support '.' for navigation properties return TryGetComplexMember(_value, memberName, out result); } public override string ToString() { return _value.ToString(); } private bool TryGetRowIndex(string memberName, out object result) { result = null; if (String.IsNullOrEmpty(memberName)) { return false; } if (memberName == RowIndexMemberName) { result = _rowIndex; return true; } return false; } private static bool TryGetComplexMember(object obj, string name, out object result) { result = null; string[] names = name.Split('.'); for (int i = 0; i < names.Length; i++) { if ((obj == null) || !TryGetMember(obj, names[i], out result)) { result = null; return false; } obj = result; } return true; } private static bool TryGetMember(object obj, string name, out object result) { PropertyInfo property = obj.GetType().GetProperty(name, BindFlags); if ((property != null) && (property.GetIndexParameters().Length == 0)) { result = property.GetValue(obj, null); return true; } result = null; return false; } } } ================================================ FILE: src/System.Web.Helpers/WebGrid/_WebGridRenderer.cshtml ================================================ @using System.Globalization @using System.Text @using System.Web.Helpers.Resources @using System.Web.Mvc @using System.Web.WebPages.Html @using System.Web.WebPages.Scope @using Microsoft.Internal.Web.Utils @helper GridInitScript(WebGrid webGrid, HttpContextBase httpContext) { if (!webGrid.IsAjaxEnabled) { return; } if (!IsGridScriptRendered(httpContext)) { SetGridScriptRendered(httpContext, true); } } @helper Table(WebGrid webGrid, HttpContextBase httpContext, string tableStyle, string headerStyle, string footerStyle, string rowStyle, string alternatingRowStyle, string selectedRowStyle, string caption, bool displayHeader, bool fillEmptyRows, string emptyRowCellValue, IEnumerable columns, IEnumerable exclusions, Func footer, object htmlAttributes) { if (emptyRowCellValue == null) { emptyRowCellValue = " "; } @GridInitScript(webGrid, httpContext) var htmlAttributeDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes); if (webGrid.IsAjaxEnabled) { htmlAttributeDictionary["data-swhgajax"] = "true"; htmlAttributeDictionary["data-swhgcontainer"] = webGrid.AjaxUpdateContainerId; htmlAttributeDictionary["data-swhgcallback"] = webGrid.AjaxUpdateCallback; } @if (!caption.IsEmpty()) { @caption } @if (displayHeader) { @foreach (var column in columns) { @if (ShowSortableColumnHeader(webGrid, column)) { var text = column.Header.IsEmpty() ? column.ColumnName : column.Header; @GridLink(webGrid, webGrid.GetSortUrl(column.ColumnName), text) } else { @(column.Header ?? column.ColumnName) } } } @if (footer != null) { @Format(footer, null) } @{ int rowIndex = 0; } @foreach (var row in webGrid.Rows) { string style = GetRowStyle(webGrid, rowIndex++, rowStyle, alternatingRowStyle, selectedRowStyle); @foreach (var column in columns) { var value = (column.Format == null) ? HttpUtility.HtmlEncode(row[column.ColumnName]) : Format(column.Format, row).ToString(); @Raw(value) } } @if (fillEmptyRows) { rowIndex = webGrid.Rows.Count; while (rowIndex < webGrid.RowsPerPage) { string style = GetRowStyle(webGrid, rowIndex++, rowStyle, alternatingRowStyle, null); @foreach (var column in columns) { @Raw(emptyRowCellValue) } } } } @helper Pager( WebGrid webGrid, HttpContextBase httpContext, WebGridPagerModes mode, string firstText, string previousText, string nextText, string lastText, int numericLinksCount, bool renderAjaxContainer) { int currentPage = webGrid.PageIndex; int totalPages = webGrid.PageCount; int lastPage = totalPages - 1; @GridInitScript(webGrid, httpContext) if (renderAjaxContainer && webGrid.IsAjaxEnabled) { @: } if (ModeEnabled(mode, WebGridPagerModes.FirstLast) && currentPage > 1) { if (String.IsNullOrEmpty(firstText)) { firstText = "<<"; } @GridLink(webGrid, webGrid.GetPageUrl(0), firstText) @Raw(" ") } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious) && currentPage > 0) { if (String.IsNullOrEmpty(previousText)) { previousText = "<"; } @GridLink(webGrid, webGrid.GetPageUrl(currentPage - 1), previousText) @Raw(" ") } if (ModeEnabled(mode, WebGridPagerModes.Numeric) && (totalPages > 1)) { int last = currentPage + (numericLinksCount / 2); int first = last - numericLinksCount + 1; if (last > lastPage) { first -= last - lastPage; last = lastPage; } if (first < 0) { last = Math.Min(last + (0 - first), lastPage); first = 0; } for (int i = first; i <= last; i++) { var pageText = (i + 1).ToString(CultureInfo.InvariantCulture); if (i == currentPage) { @pageText } else { @GridLink(webGrid, webGrid.GetPageUrl(i), pageText) } @Raw(" ") } } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious) && (currentPage < lastPage)) { if (String.IsNullOrEmpty(nextText)) { nextText = ">"; } @GridLink(webGrid, webGrid.GetPageUrl(currentPage + 1), nextText) @Raw(" ") } if (ModeEnabled(mode, WebGridPagerModes.FirstLast) && (currentPage < lastPage - 1)) { if (String.IsNullOrEmpty(lastText)) { lastText = ">>"; } @GridLink(webGrid, webGrid.GetPageUrl(lastPage), lastText) } if (renderAjaxContainer && webGrid.IsAjaxEnabled) { @: } } @functions{ private static readonly object _gridScriptRenderedKey = new object(); private static bool IsGridScriptRendered(HttpContextBase context) { bool? value = (bool?)context.Items[_gridScriptRenderedKey]; return value.HasValue && value.Value; } private static void SetGridScriptRendered(HttpContextBase context, bool value) { context.Items[_gridScriptRenderedKey] = value; } private static bool ShowSortableColumnHeader(WebGrid grid, WebGridColumn column) { return grid.CanSort && column.CanSort && !column.ColumnName.IsEmpty(); } public static IHtmlString GridLink(WebGrid webGrid, string url, string text) { TagBuilder builder = new TagBuilder("a"); builder.SetInnerText(text); builder.MergeAttribute("href", url); if (webGrid.IsAjaxEnabled) { builder.MergeAttribute("data-swhglnk", "true"); } return builder.ToHtmlString(TagRenderMode.Normal); } private static IHtmlString Raw(string text) { return new HtmlString(text); } private static IHtmlString RawJS(string text) { return new HtmlString(HttpUtility.JavaScriptStringEncode(text)); } private static IHtmlString CssClass(string className) { return new HtmlString((!className.IsEmpty()) ? " class=\"" + HttpUtility.HtmlAttributeEncode(className) + "\"" : String.Empty); } private static string GetRowStyle(WebGrid webGrid, int rowIndex, string rowStyle, string alternatingRowStyle, string selectedRowStyle) { StringBuilder style = new StringBuilder(); if (rowIndex % 2 == 0) { if (!String.IsNullOrEmpty(rowStyle)) { style.Append(rowStyle); } } else { if (!String.IsNullOrEmpty(alternatingRowStyle)) { style.Append(alternatingRowStyle); } } if (!String.IsNullOrEmpty(selectedRowStyle) && (rowIndex == webGrid.SelectedIndex)) { if (style.Length > 0) { style.Append(" "); } style.Append(selectedRowStyle); } return style.ToString(); } private static HelperResult Format(Func format, dynamic arg) { var result = format(arg); return new HelperResult(tw => { var helper = result as HelperResult; if (helper != null) { helper.WriteTo(tw); return; } IHtmlString htmlString = result as IHtmlString; if (htmlString != null) { tw.Write(htmlString); return; } if (result != null) { tw.Write(HttpUtility.HtmlEncode(result)); } }); } private static IHtmlString PrintAttributes(IDictionary attributes) { var builder = new StringBuilder(); foreach (var item in attributes) { var value = Convert.ToString(item.Value, CultureInfo.InvariantCulture); builder.Append(' ') .Append(HttpUtility.HtmlEncode(item.Key)) .Append("=\"") .Append(HttpUtility.HtmlAttributeEncode(value)) .Append('"'); } return new HtmlString(builder.ToString()); } private static bool ModeEnabled(WebGridPagerModes mode, WebGridPagerModes modeCheck) { return (mode & modeCheck) == modeCheck; } } ================================================ FILE: src/System.Web.Helpers/WebGrid/_WebGridRenderer.generated.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.214 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Web.Mvc; using System.Web.WebPages; using System.Web.WebPages.Html; namespace System.Web.Helpers { [GeneratedCode("RazorSingleFileGenerator", "1.0.0.0")] internal class WebGridRenderer : HelperPage { #line hidden #line hidden public static HelperResult GridInitScript(WebGrid webGrid, HttpContextBase httpContext) { return new HelperResult(__razor_helper_writer => { if (!webGrid.IsAjaxEnabled) { return; } if (!IsGridScriptRendered(httpContext)) { SetGridScriptRendered(httpContext, true); WriteLiteralTo(@__razor_helper_writer, " "; internal static readonly string ClientValidationKeyName = "ClientValidationEnabled"; internal static readonly string UnobtrusiveJavaScriptKeyName = "UnobtrusiveJavaScriptEnabled"; internal static readonly string ValidationSummaryMessageElementKeyName = "ValidationSummaryMessageElement"; internal static readonly string ValidationMessageElementKeyName = "ValidationMessageElement"; // Some values have to be stored in HttpContext.Items in order to be propagated between calls // to RenderPartial(), RenderAction(), etc. private static readonly object _formContextKey = new object(); private static readonly object _lastFormNumKey = new object(); private Func> _scopeThunk; private IDictionary _transientScope; private DynamicViewDataDictionary _dynamicViewDataDictionary; private Func _formIdGenerator; // We need a default FormContext if the user uses html
instead of an MvcForm private FormContext _defaultFormContext = new FormContext(); // parameterless constructor used for mocking public ViewContext() { } [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The virtual property setters are only to support mocking frameworks, in which case this constructor shouldn't be called anyway.")] public ViewContext(ControllerContext controllerContext, IView view, ViewDataDictionary viewData, TempDataDictionary tempData, TextWriter writer) : base(controllerContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (view == null) { throw new ArgumentNullException("view"); } if (viewData == null) { throw new ArgumentNullException("viewData"); } if (tempData == null) { throw new ArgumentNullException("tempData"); } if (writer == null) { throw new ArgumentNullException("writer"); } View = view; ViewData = viewData; Writer = writer; TempData = tempData; } public virtual bool ClientValidationEnabled { get { return GetClientValidationEnabled(Scope, HttpContext); } set { SetClientValidationEnabled(value, Scope, HttpContext); } } public virtual FormContext FormContext { get { // Never return a null form context, this is important for validation purposes return HttpContext.Items[_formContextKey] as FormContext ?? _defaultFormContext; } set { HttpContext.Items[_formContextKey] = value; } } internal Func FormIdGenerator { get { if (_formIdGenerator == null) { _formIdGenerator = DefaultFormIdGenerator; } return _formIdGenerator; } set { _formIdGenerator = value; } } internal static Func> GlobalScopeThunk { get; set; } private IDictionary Scope { get { if (ScopeThunk != null) { return ScopeThunk(); } if (_transientScope == null) { _transientScope = new Dictionary(); } return _transientScope; } } internal Func> ScopeThunk { get { return _scopeThunk ?? GlobalScopeThunk; } set { _scopeThunk = value; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "The property setter is only here to support mocking this type and should not be called at runtime.")] public virtual TempDataDictionary TempData { get; set; } public virtual bool UnobtrusiveJavaScriptEnabled { get { return GetUnobtrusiveJavaScriptEnabled(Scope, HttpContext); } set { SetUnobtrusiveJavaScriptEnabled(value, Scope, HttpContext); } } /// /// Element name used to wrap a top-level message generated by /// and other overloads. /// public virtual string ValidationSummaryMessageElement { get { return GetValidationSummaryMessageElement(Scope, HttpContext); } set { if (String.IsNullOrEmpty(value)) { throw Error.ParameterCannotBeNullOrEmpty("value"); } SetValidationSummaryMessageElement(value, Scope, HttpContext); } } /// /// Element name used to wrap a top-level message generated by /// and other overloads. /// public virtual string ValidationMessageElement { get { return GetValidationMessageElement(Scope, HttpContext); } set { if (String.IsNullOrEmpty(value)) { throw Error.ParameterCannotBeNullOrEmpty("value"); } SetValidationMessageElement(value, Scope, HttpContext); } } public virtual IView View { get; set; } public dynamic ViewBag { get { if (_dynamicViewDataDictionary == null) { _dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData); } return _dynamicViewDataDictionary; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "The property setter is only here to support mocking this type and should not be called at runtime.")] public virtual ViewDataDictionary ViewData { get; set; } public virtual TextWriter Writer { get; set; } private string DefaultFormIdGenerator() { int formNum = IncrementFormCount(HttpContext.Items); return String.Format(CultureInfo.InvariantCulture, "form{0}", formNum); } internal static bool GetClientValidationEnabled(IDictionary scope = null, HttpContextBase httpContext = null) { return ScopeCache.Get(scope, httpContext).ClientValidationEnabled; } internal FormContext GetFormContextForClientValidation() { return (ClientValidationEnabled) ? FormContext : null; } internal static bool GetUnobtrusiveJavaScriptEnabled(IDictionary scope = null, HttpContextBase httpContext = null) { return ScopeCache.Get(scope, httpContext).UnobtrusiveJavaScriptEnabled; } internal static string GetValidationSummaryMessageElement( IDictionary scope = null, HttpContextBase httpContext = null) { return ScopeCache.Get(scope, httpContext).ValidationSummaryMessageElement; } internal static string GetValidationMessageElement( IDictionary scope = null, HttpContextBase httpContext = null) { return ScopeCache.Get(scope, httpContext).ValidationMessageElement; } private static int IncrementFormCount(IDictionary items) { object lastFormNum = items[_lastFormNumKey]; int newFormNum = (lastFormNum != null) ? ((int)lastFormNum) + 1 : 0; items[_lastFormNumKey] = newFormNum; return newFormNum; } public void OutputClientValidation() { FormContext formContext = GetFormContextForClientValidation(); if (formContext == null || UnobtrusiveJavaScriptEnabled) { return; // do nothing } string scriptWithCorrectNewLines = ClientValidationScript.Replace("\r\n", Environment.NewLine); string validationJson = formContext.GetJsonValidationMetadata(); string formatted = String.Format(CultureInfo.InvariantCulture, scriptWithCorrectNewLines, validationJson); Writer.Write(formatted); } internal static void SetClientValidationEnabled(bool enabled, IDictionary scope = null, HttpContextBase httpContext = null) { ScopeCache.Get(scope, httpContext).ClientValidationEnabled = enabled; } internal static void SetUnobtrusiveJavaScriptEnabled(bool enabled, IDictionary scope = null, HttpContextBase httpContext = null) { ScopeCache.Get(scope, httpContext).UnobtrusiveJavaScriptEnabled = enabled; } internal static void SetValidationSummaryMessageElement( string elementName, IDictionary scope = null, HttpContextBase httpContext = null) { ScopeCache.Get(scope, httpContext).ValidationSummaryMessageElement = elementName; } internal static void SetValidationMessageElement( string elementName, IDictionary scope = null, HttpContextBase httpContext = null) { ScopeCache.Get(scope, httpContext).ValidationMessageElement = elementName; } private static TValue ScopeGet(IDictionary scope, string name, TValue defaultValue = default(TValue)) { object result; if (scope.TryGetValue(name, out result)) { return (TValue)Convert.ChangeType(result, typeof(TValue), CultureInfo.InvariantCulture); } return defaultValue; } private sealed class ScopeCache { private static readonly object _cacheKey = new object(); private bool _clientValidationEnabled; private IDictionary _scope; private bool _unobtrusiveJavaScriptEnabled; private string _validationSummaryMessageElement; private string _validationMessageElement; private ScopeCache(IDictionary scope) { _scope = scope; _clientValidationEnabled = ScopeGet(scope, ClientValidationKeyName, false); _unobtrusiveJavaScriptEnabled = ScopeGet(scope, UnobtrusiveJavaScriptKeyName, false); _validationSummaryMessageElement = ScopeGet(scope, ValidationSummaryMessageElementKeyName, "span"); _validationMessageElement = ScopeGet(scope, ValidationMessageElementKeyName, "span"); } public bool ClientValidationEnabled { get { return _clientValidationEnabled; } set { _clientValidationEnabled = value; _scope[ClientValidationKeyName] = value; } } public bool UnobtrusiveJavaScriptEnabled { get { return _unobtrusiveJavaScriptEnabled; } set { _unobtrusiveJavaScriptEnabled = value; _scope[UnobtrusiveJavaScriptKeyName] = value; } } public string ValidationSummaryMessageElement { get { return _validationSummaryMessageElement; } set { _validationSummaryMessageElement = value; _scope[ValidationSummaryMessageElementKeyName] = value; } } public string ValidationMessageElement { get { return _validationMessageElement; } set { _validationMessageElement = value; _scope[ValidationMessageElementKeyName] = value; } } public static ScopeCache Get(IDictionary scope, HttpContextBase httpContext) { if (httpContext == null && Web.HttpContext.Current != null) { httpContext = new HttpContextWrapper(Web.HttpContext.Current); } ScopeCache result = null; scope = scope ?? ScopeStorage.CurrentScope; if (httpContext != null) { result = httpContext.Items[_cacheKey] as ScopeCache; } if (result == null || result._scope != scope) { result = new ScopeCache(scope); if (httpContext != null) { httpContext.Items[_cacheKey] = result; } } return result; } } } } ================================================ FILE: src/System.Web.Mvc/ViewDataDictionary.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Web.Mvc.Properties; namespace System.Web.Mvc { // TODO: Unit test ModelState interaction with VDD public class ViewDataDictionary : IDictionary { private readonly IDictionary _innerDictionary; private readonly ModelStateDictionary _modelState; private object _model; private ModelMetadata _modelMetadata; private TemplateInfo _templateMetadata; public ViewDataDictionary() : this((object)null) { } [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "See note on SetModel() method.")] public ViewDataDictionary(object model) { Model = model; _innerDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); _modelState = new ModelStateDictionary(); } [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "See note on SetModel() method.")] public ViewDataDictionary(ViewDataDictionary dictionary) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } _innerDictionary = new CopyOnWriteDictionary(dictionary, StringComparer.OrdinalIgnoreCase); _modelState = new ModelStateDictionary(dictionary.ModelState); Model = dictionary.Model; TemplateInfo = dictionary.TemplateInfo; // PERF: Don't unnecessarily instantiate the model metadata _modelMetadata = dictionary._modelMetadata; } public int Count { get { return _innerDictionary.Count; } } public bool IsReadOnly { get { return _innerDictionary.IsReadOnly; } } public ICollection Keys { get { return _innerDictionary.Keys; } } public object Model { get { return _model; } set { _modelMetadata = null; SetModel(value); } } public virtual ModelMetadata ModelMetadata { get { if (_modelMetadata == null && _model != null) { _modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => _model, _model.GetType()); } return _modelMetadata; } set { _modelMetadata = value; } } public ModelStateDictionary ModelState { get { return _modelState; } } public TemplateInfo TemplateInfo { get { if (_templateMetadata == null) { _templateMetadata = new TemplateInfo(); } return _templateMetadata; } set { _templateMetadata = value; } } public ICollection Values { get { return _innerDictionary.Values; } } public object this[string key] { get { object value; _innerDictionary.TryGetValue(key, out value); return value; } set { _innerDictionary[key] = value; } } // For unit testing internal IDictionary InnerDictionary { get { return _innerDictionary; } } public void Add(KeyValuePair item) { _innerDictionary.Add(item); } public void Add(string key, object value) { _innerDictionary.Add(key, value); } public void Clear() { _innerDictionary.Clear(); } public bool Contains(KeyValuePair item) { return _innerDictionary.Contains(item); } public bool ContainsKey(string key) { return _innerDictionary.ContainsKey(key); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { _innerDictionary.CopyTo(array, arrayIndex); } public object Eval(string expression) { ViewDataInfo info = GetViewDataInfo(expression); return (info != null) ? info.Value : null; } public string Eval(string expression, string format) { object value = Eval(expression); return FormatValueInternal(value, format); } internal static string FormatValueInternal(object value, string format) { if (value == null) { return String.Empty; } if (String.IsNullOrEmpty(format)) { return Convert.ToString(value, CultureInfo.CurrentCulture); } else { return String.Format(CultureInfo.CurrentCulture, format, value); } } public IEnumerator> GetEnumerator() { return _innerDictionary.GetEnumerator(); } public ViewDataInfo GetViewDataInfo(string expression) { if (String.IsNullOrEmpty(expression)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "expression"); } return ViewDataEvaluator.Eval(this, expression); } public bool Remove(KeyValuePair item) { return _innerDictionary.Remove(item); } public bool Remove(string key) { return _innerDictionary.Remove(key); } // This method will execute before the derived type's instance constructor executes. Derived types must // be aware of this and should plan accordingly. For example, the logic in SetModel() should be simple // enough so as not to depend on the "this" pointer referencing a fully constructed object. protected virtual void SetModel(object value) { _model = value; } public bool TryGetValue(string key, out object value) { return _innerDictionary.TryGetValue(key, out value); } internal static class ViewDataEvaluator { public static ViewDataInfo Eval(ViewDataDictionary vdd, string expression) { //Given an expression "foo.bar.baz" we look up the following (pseudocode): // this["foo.bar.baz.quux"] // this["foo.bar.baz"]["quux"] // this["foo.bar"]["baz.quux] // this["foo.bar"]["baz"]["quux"] // this["foo"]["bar.baz.quux"] // this["foo"]["bar.baz"]["quux"] // this["foo"]["bar"]["baz.quux"] // this["foo"]["bar"]["baz"]["quux"] ViewDataInfo evaluated = EvalComplexExpression(vdd, expression); return evaluated; } private static ViewDataInfo EvalComplexExpression(object indexableObject, string expression) { foreach (ExpressionPair expressionPair in GetRightToLeftExpressions(expression)) { string subExpression = expressionPair.Left; string postExpression = expressionPair.Right; ViewDataInfo subTargetInfo = GetPropertyValue(indexableObject, subExpression); if (subTargetInfo != null) { if (String.IsNullOrEmpty(postExpression)) { return subTargetInfo; } if (subTargetInfo.Value != null) { ViewDataInfo potential = EvalComplexExpression(subTargetInfo.Value, postExpression); if (potential != null) { return potential; } } } } return null; } private static IEnumerable GetRightToLeftExpressions(string expression) { // Produces an enumeration of all the combinations of complex property names // given a complex expression. See the list above for an example of the result // of the enumeration. yield return new ExpressionPair(expression, String.Empty); int lastDot = expression.LastIndexOf('.'); string subExpression = expression; string postExpression = String.Empty; while (lastDot > -1) { subExpression = expression.Substring(0, lastDot); postExpression = expression.Substring(lastDot + 1); yield return new ExpressionPair(subExpression, postExpression); lastDot = subExpression.LastIndexOf('.'); } } private static ViewDataInfo GetIndexedPropertyValue(object indexableObject, string key) { IDictionary dict = indexableObject as IDictionary; object value = null; bool success = false; if (dict != null) { success = dict.TryGetValue(key, out value); } else { TryGetValueDelegate tgvDel = TypeHelpers.CreateTryGetValueDelegate(indexableObject.GetType()); if (tgvDel != null) { success = tgvDel(indexableObject, key, out value); } } if (success) { return new ViewDataInfo() { Container = indexableObject, Value = value }; } return null; } private static ViewDataInfo GetPropertyValue(object container, string propertyName) { // This method handles one "segment" of a complex property expression // First, we try to evaluate the property based on its indexer ViewDataInfo value = GetIndexedPropertyValue(container, propertyName); if (value != null) { return value; } // If the indexer didn't return anything useful, continue... // If the container is a ViewDataDictionary then treat its Model property // as the container instead of the ViewDataDictionary itself. ViewDataDictionary vdd = container as ViewDataDictionary; if (vdd != null) { container = vdd.Model; } // If the container is null, we're out of options if (container == null) { return null; } // Second, we try to use PropertyDescriptors and treat the expression as a property name PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container).Find(propertyName, true); if (descriptor == null) { return null; } return new ViewDataInfo(() => descriptor.GetValue(container)) { Container = container, PropertyDescriptor = descriptor }; } private struct ExpressionPair { public readonly string Left; public readonly string Right; public ExpressionPair(string left, string right) { Left = left; Right = right; } } } #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return _innerDictionary.GetEnumerator(); } #endregion } } ================================================ FILE: src/System.Web.Mvc/ViewDataDictionaryOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public class ViewDataDictionary : ViewDataDictionary { public ViewDataDictionary() : base(default(TModel)) { } public ViewDataDictionary(TModel model) : base(model) { } public ViewDataDictionary(ViewDataDictionary viewDataDictionary) : base(viewDataDictionary) { } public new TModel Model { get { return (TModel)base.Model; } set { SetModel(value); } } public override ModelMetadata ModelMetadata { get { ModelMetadata result = base.ModelMetadata; if (result == null) { result = base.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)); } return result; } set { base.ModelMetadata = value; } } protected override void SetModel(object value) { bool castWillSucceed = TypeHelpers.IsCompatibleObject(value); if (castWillSucceed) { base.SetModel((TModel)value); } else { InvalidOperationException exception = (value != null) ? Error.ViewDataDictionary_WrongTModelType(value.GetType(), typeof(TModel)) : Error.ViewDataDictionary_ModelCannotBeNull(typeof(TModel)); throw exception; } } } } ================================================ FILE: src/System.Web.Mvc/ViewDataInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; namespace System.Web.Mvc { public class ViewDataInfo { private object _value; private Func _valueAccessor; public ViewDataInfo() { } public ViewDataInfo(Func valueAccessor) { _valueAccessor = valueAccessor; } public object Container { get; set; } public PropertyDescriptor PropertyDescriptor { get; set; } public object Value { get { if (_valueAccessor != null) { _value = _valueAccessor(); _valueAccessor = null; } return _value; } set { _value = value; _valueAccessor = null; } } } } ================================================ FILE: src/System.Web.Mvc/ViewEngineCollection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Web.Mvc.Properties; namespace System.Web.Mvc { public class ViewEngineCollection : Collection { private IViewEngine[] _combinedItems; private IDependencyResolver _dependencyResolver; public ViewEngineCollection() { } public ViewEngineCollection(IList list) : base(list) { } internal ViewEngineCollection(IList list, IDependencyResolver dependencyResolver) : base(list) { _dependencyResolver = dependencyResolver; } internal IViewEngine[] CombinedItems { get { IViewEngine[] combinedItems = _combinedItems; if (combinedItems == null) { combinedItems = MultiServiceResolver.GetCombined(Items, _dependencyResolver); _combinedItems = combinedItems; } return combinedItems; } } protected override void ClearItems() { _combinedItems = null; base.ClearItems(); } protected override void InsertItem(int index, IViewEngine item) { if (item == null) { throw new ArgumentNullException("item"); } _combinedItems = null; base.InsertItem(index, item); } protected override void RemoveItem(int index) { _combinedItems = null; base.RemoveItem(index); } protected override void SetItem(int index, IViewEngine item) { if (item == null) { throw new ArgumentNullException("item"); } _combinedItems = null; base.SetItem(index, item); } private ViewEngineResult Find(Func cacheLocator, Func locator) { // First, look up using the cacheLocator and do not track the searched paths in non-matching view engines // Then, look up using the normal locator and track the searched paths so that an error view engine can be returned return Find(cacheLocator, trackSearchedPaths: false) ?? Find(locator, trackSearchedPaths: true); } private ViewEngineResult Find(Func lookup, bool trackSearchedPaths) { // Returns // 1st result // OR list of searched paths (if trackSearchedPaths == true) // OR null ViewEngineResult result; List searched = null; if (trackSearchedPaths) { searched = new List(); } foreach (IViewEngine engine in CombinedItems) { if (engine != null) { result = lookup(engine); if (result.View != null) { return result; } if (trackSearchedPaths) { searched.AddRange(result.SearchedLocations); } } } if (trackSearchedPaths) { // Remove duplicate search paths since multiple view engines could have potentially looked at the same path return new ViewEngineResult(searched.Distinct().ToList()); } else { return null; } } public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(partialViewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName"); } return Find(e => e.FindPartialView(controllerContext, partialViewName, true), e => e.FindPartialView(controllerContext, partialViewName, false)); } public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(viewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName"); } return Find(e => e.FindView(controllerContext, viewName, masterName, true), e => e.FindView(controllerContext, viewName, masterName, false)); } } } ================================================ FILE: src/System.Web.Mvc/ViewEngineResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace System.Web.Mvc { public class ViewEngineResult { public ViewEngineResult(IEnumerable searchedLocations) { if (searchedLocations == null) { throw new ArgumentNullException("searchedLocations"); } SearchedLocations = searchedLocations; } public ViewEngineResult(IView view, IViewEngine viewEngine) { if (view == null) { throw new ArgumentNullException("view"); } if (viewEngine == null) { throw new ArgumentNullException("viewEngine"); } View = view; ViewEngine = viewEngine; } public IEnumerable SearchedLocations { get; private set; } public IView View { get; private set; } public IViewEngine ViewEngine { get; private set; } } } ================================================ FILE: src/System.Web.Mvc/ViewEngines.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public static class ViewEngines { private static readonly ViewEngineCollection _engines = new ViewEngineCollection { new WebFormViewEngine(), new RazorViewEngine(), }; public static ViewEngineCollection Engines { get { return _engines; } } } } ================================================ FILE: src/System.Web.Mvc/ViewMasterPage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web.Mvc.Properties; using System.Web.UI; namespace System.Web.Mvc { [FileLevelControlBuilder(typeof(ViewMasterPageControlBuilder))] public class ViewMasterPage : MasterPage { public AjaxHelper Ajax { get { return ViewPage.Ajax; } } public HtmlHelper Html { get { return ViewPage.Html; } } public object Model { get { return ViewData.Model; } } public TempDataDictionary TempData { get { return ViewPage.TempData; } } public UrlHelper Url { get { return ViewPage.Url; } } public dynamic ViewBag { get { return ViewPage.ViewBag; } } public ViewContext ViewContext { get { return ViewPage.ViewContext; } } public ViewDataDictionary ViewData { get { return ViewPage.ViewData; } } internal ViewPage ViewPage { get { ViewPage viewPage = Page as ViewPage; if (viewPage == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.ViewMasterPage_RequiresViewPage)); } return viewPage; } } public HtmlTextWriter Writer { get { return ViewPage.Writer; } } } } ================================================ FILE: src/System.Web.Mvc/ViewMasterPageControlBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.UI; namespace System.Web.Mvc { internal sealed class ViewMasterPageControlBuilder : FileLevelMasterPageControlBuilder, IMvcControlBuilder { public string Inherits { get; set; } public override void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod) { if (!String.IsNullOrWhiteSpace(Inherits)) { derivedType.BaseTypes[0] = new CodeTypeReference(Inherits); } } } } ================================================ FILE: src/System.Web.Mvc/ViewMasterPageOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public class ViewMasterPage : ViewMasterPage { private AjaxHelper _ajaxHelper; private HtmlHelper _htmlHelper; private ViewDataDictionary _viewData; public new AjaxHelper Ajax { get { if (_ajaxHelper == null) { _ajaxHelper = new AjaxHelper(ViewContext, ViewPage); } return _ajaxHelper; } } public new HtmlHelper Html { get { if (_htmlHelper == null) { _htmlHelper = new HtmlHelper(ViewContext, ViewPage); } return _htmlHelper; } } public new TModel Model { get { return ViewData.Model; } } public new ViewDataDictionary ViewData { get { if (_viewData == null) { _viewData = new ViewDataDictionary(ViewPage.ViewData); } return _viewData; } } } } ================================================ FILE: src/System.Web.Mvc/ViewPage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text; using System.Web.UI; namespace System.Web.Mvc { [FileLevelControlBuilder(typeof(ViewPageControlBuilder))] public class ViewPage : Page, IViewDataContainer { [ThreadStatic] private static int _nextId; private DynamicViewDataDictionary _dynamicViewData; private string _masterLocation; private ViewDataDictionary _viewData; public AjaxHelper Ajax { get; set; } public HtmlHelper Html { get; set; } public string MasterLocation { get { return _masterLocation ?? String.Empty; } set { _masterLocation = value; } } public object Model { get { return ViewData.Model; } } public TempDataDictionary TempData { get { return ViewContext.TempData; } } public UrlHelper Url { get; set; } public dynamic ViewBag { get { if (_dynamicViewData == null) { _dynamicViewData = new DynamicViewDataDictionary(() => ViewData); } return _dynamicViewData; } } public ViewContext ViewContext { get; set; } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is the mechanism by which the ViewPage gets its ViewDataDictionary object.")] public ViewDataDictionary ViewData { get { if (_viewData == null) { SetViewData(new ViewDataDictionary()); } return _viewData; } set { SetViewData(value); } } public HtmlTextWriter Writer { get; private set; } public virtual void InitHelpers() { Ajax = new AjaxHelper(ViewContext, this); Html = new HtmlHelper(ViewContext, this); Url = new UrlHelper(ViewContext.RequestContext); } internal static string NextId() { return (++_nextId).ToString(CultureInfo.InvariantCulture); } protected override void OnPreInit(EventArgs e) { base.OnPreInit(e); if (!String.IsNullOrEmpty(MasterLocation)) { MasterPageFile = MasterLocation; } } public override void ProcessRequest(HttpContext context) { // Tracing requires IDs to be unique. ID = NextId(); base.ProcessRequest(context); } protected override void Render(HtmlTextWriter writer) { Writer = writer; try { base.Render(writer); } finally { Writer = null; } } [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The object is disposed in the finally block of the method")] public virtual void RenderView(ViewContext viewContext) { ViewContext = viewContext; InitHelpers(); bool createdSwitchWriter = false; SwitchWriter switchWriter = viewContext.HttpContext.Response.Output as SwitchWriter; try { if (switchWriter == null) { switchWriter = new SwitchWriter(); createdSwitchWriter = true; } using (switchWriter.Scope(viewContext.Writer)) { if (createdSwitchWriter) { // It's safe to reset the _nextId within a Server.Execute() since it pushes a new TraceContext onto // the stack, so there won't be an ID conflict. int originalNextId = _nextId; try { _nextId = 0; viewContext.HttpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(this), switchWriter, true /* preserveForm */); } finally { // Restore the original _nextId in case this isn't actually the outermost view, since resetting // the _nextId may now cause trace ID conflicts in the outer view. _nextId = originalNextId; } } else { ProcessRequest(HttpContext.Current); } } } finally { if (createdSwitchWriter) { switchWriter.Dispose(); } } } [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textWriter", Justification = "This method existed in MVC 1.0 and has been deprecated.")] [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method existed in MVC 1.0 and has been deprecated.")] [Obsolete("The TextWriter is now provided by the ViewContext object passed to the RenderView method.", true /* error */)] public void SetTextWriter(TextWriter textWriter) { // this is now a no-op } protected virtual void SetViewData(ViewDataDictionary viewData) { _viewData = viewData; } internal class SwitchWriter : TextWriter { public SwitchWriter() : base(CultureInfo.CurrentCulture) { } public override Encoding Encoding { get { return InnerWriter.Encoding; } } public override IFormatProvider FormatProvider { get { return InnerWriter.FormatProvider; } } internal TextWriter InnerWriter { get; set; } public override string NewLine { get { return InnerWriter.NewLine; } set { InnerWriter.NewLine = value; } } public override void Close() { InnerWriter.Close(); } public override void Flush() { InnerWriter.Flush(); } public IDisposable Scope(TextWriter writer) { WriterScope scope = new WriterScope(this, InnerWriter); try { if (writer != this) { InnerWriter = writer; } return scope; } catch { scope.Dispose(); throw; } } public override void Write(bool value) { InnerWriter.Write(value); } public override void Write(char value) { InnerWriter.Write(value); } public override void Write(char[] buffer) { InnerWriter.Write(buffer); } public override void Write(char[] buffer, int index, int count) { InnerWriter.Write(buffer, index, count); } public override void Write(decimal value) { InnerWriter.Write(value); } public override void Write(double value) { InnerWriter.Write(value); } public override void Write(float value) { InnerWriter.Write(value); } public override void Write(int value) { InnerWriter.Write(value); } public override void Write(long value) { InnerWriter.Write(value); } public override void Write(object value) { InnerWriter.Write(value); } public override void Write(string format, object arg0) { InnerWriter.Write(format, arg0); } public override void Write(string format, object arg0, object arg1) { InnerWriter.Write(format, arg0, arg1); } public override void Write(string format, object arg0, object arg1, object arg2) { InnerWriter.Write(format, arg0, arg1, arg2); } public override void Write(string format, params object[] arg) { InnerWriter.Write(format, arg); } public override void Write(string value) { InnerWriter.Write(value); } public override void Write(uint value) { InnerWriter.Write(value); } public override void Write(ulong value) { InnerWriter.Write(value); } public override void WriteLine() { InnerWriter.WriteLine(); } public override void WriteLine(bool value) { InnerWriter.WriteLine(value); } public override void WriteLine(char value) { InnerWriter.WriteLine(value); } public override void WriteLine(char[] buffer) { InnerWriter.WriteLine(buffer); } public override void WriteLine(char[] buffer, int index, int count) { InnerWriter.WriteLine(buffer, index, count); } public override void WriteLine(decimal value) { InnerWriter.WriteLine(value); } public override void WriteLine(double value) { InnerWriter.WriteLine(value); } public override void WriteLine(float value) { InnerWriter.WriteLine(value); } public override void WriteLine(int value) { InnerWriter.WriteLine(value); } public override void WriteLine(long value) { InnerWriter.WriteLine(value); } public override void WriteLine(object value) { InnerWriter.WriteLine(value); } public override void WriteLine(string format, object arg0) { InnerWriter.WriteLine(format, arg0); } public override void WriteLine(string format, object arg0, object arg1) { InnerWriter.WriteLine(format, arg0, arg1); } public override void WriteLine(string format, object arg0, object arg1, object arg2) { InnerWriter.WriteLine(format, arg0, arg1, arg2); } public override void WriteLine(string format, params object[] arg) { InnerWriter.WriteLine(format, arg); } public override void WriteLine(string value) { InnerWriter.WriteLine(value); } public override void WriteLine(uint value) { InnerWriter.WriteLine(value); } public override void WriteLine(ulong value) { InnerWriter.WriteLine(value); } private sealed class WriterScope : IDisposable { private SwitchWriter _switchWriter; private TextWriter _writerToRestore; public WriterScope(SwitchWriter switchWriter, TextWriter writerToRestore) { _switchWriter = switchWriter; _writerToRestore = writerToRestore; } public void Dispose() { _switchWriter.InnerWriter = _writerToRestore; } } } } } ================================================ FILE: src/System.Web.Mvc/ViewPageControlBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.UI; namespace System.Web.Mvc { internal sealed class ViewPageControlBuilder : FileLevelPageControlBuilder, IMvcControlBuilder { public string Inherits { get; set; } public override void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod) { if (!String.IsNullOrWhiteSpace(Inherits)) { derivedType.BaseTypes[0] = new CodeTypeReference(Inherits); } } } } ================================================ FILE: src/System.Web.Mvc/ViewPageOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace System.Web.Mvc { public class ViewPage : ViewPage { private ViewDataDictionary _viewData; public new AjaxHelper Ajax { get; set; } public new HtmlHelper Html { get; set; } public new TModel Model { get { return ViewData.Model; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is settable for unit testing purposes")] public new ViewDataDictionary ViewData { get { if (_viewData == null) { SetViewData(new ViewDataDictionary()); } return _viewData; } set { SetViewData(value); } } public override void InitHelpers() { base.InitHelpers(); Ajax = new AjaxHelper(ViewContext, this); Html = new HtmlHelper(ViewContext, this); } protected override void SetViewData(ViewDataDictionary viewData) { _viewData = new ViewDataDictionary(viewData); base.SetViewData(_viewData); } } } ================================================ FILE: src/System.Web.Mvc/ViewResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Text; using System.Web.Mvc.Properties; namespace System.Web.Mvc { public class ViewResult : ViewResultBase { private string _masterName; public string MasterName { get { return _masterName ?? String.Empty; } set { _masterName = value; } } protected override ViewEngineResult FindView(ControllerContext context) { ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName); if (result.View != null) { return result; } // we need to generate an exception containing all the locations we searched StringBuilder locationsText = new StringBuilder(); foreach (string location in result.SearchedLocations) { locationsText.AppendLine(); locationsText.Append(location); } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.Common_ViewNotFound, ViewName, locationsText)); } } } ================================================ FILE: src/System.Web.Mvc/ViewResultBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.IO; namespace System.Web.Mvc { public abstract class ViewResultBase : ActionResult { private DynamicViewDataDictionary _dynamicViewData; private TempDataDictionary _tempData; private ViewDataDictionary _viewData; private ViewEngineCollection _viewEngineCollection; private string _viewName; public object Model { get { return ViewData.Model; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This entire type is meant to be mutable.")] public TempDataDictionary TempData { get { if (_tempData == null) { _tempData = new TempDataDictionary(); } return _tempData; } set { _tempData = value; } } public IView View { get; set; } public dynamic ViewBag { get { if (_dynamicViewData == null) { _dynamicViewData = new DynamicViewDataDictionary(() => ViewData); } return _dynamicViewData; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This entire type is meant to be mutable.")] public ViewDataDictionary ViewData { get { if (_viewData == null) { _viewData = new ViewDataDictionary(); } return _viewData; } set { _viewData = value; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This entire type is meant to be mutable.")] public ViewEngineCollection ViewEngineCollection { get { return _viewEngineCollection ?? ViewEngines.Engines; } set { _viewEngineCollection = value; } } public string ViewName { get { return _viewName ?? String.Empty; } set { _viewName = value; } } public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData.GetRequiredString("action"); } ViewEngineResult result = null; if (View == null) { result = FindView(context); View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer); View.Render(viewContext, writer); if (result != null) { result.ViewEngine.ReleaseView(context, View); } } protected abstract ViewEngineResult FindView(ControllerContext context); } } ================================================ FILE: src/System.Web.Mvc/ViewStartPage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc.Properties; using System.Web.WebPages; namespace System.Web.Mvc { public abstract class ViewStartPage : StartPage, IViewStartPageChild { private IViewStartPageChild _viewStartPageChild; public HtmlHelper Html { get { return ViewStartPageChild.Html; } } public UrlHelper Url { get { return ViewStartPageChild.Url; } } public ViewContext ViewContext { get { return ViewStartPageChild.ViewContext; } } internal IViewStartPageChild ViewStartPageChild { get { if (_viewStartPageChild == null) { IViewStartPageChild child = ChildPage as IViewStartPageChild; if (child == null) { throw new InvalidOperationException(MvcResources.ViewStartPage_RequiresMvcRazorView); } _viewStartPageChild = child; } return _viewStartPageChild; } } } } ================================================ FILE: src/System.Web.Mvc/ViewTemplateUserControl.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public class ViewTemplateUserControl : ViewTemplateUserControl { } } ================================================ FILE: src/System.Web.Mvc/ViewTemplateUserControlOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public class ViewTemplateUserControl : ViewUserControl { protected string FormattedModelValue { get { return ViewData.TemplateInfo.FormattedModelValue.ToString(); } } } } ================================================ FILE: src/System.Web.Mvc/ViewType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.UI; namespace System.Web.Mvc { [ControlBuilder(typeof(ViewTypeControlBuilder))] [NonVisualControl] public class ViewType : Control { private string _typeName; [DefaultValue("")] public string TypeName { get { return _typeName ?? String.Empty; } set { _typeName = value; } } } } ================================================ FILE: src/System.Web.Mvc/ViewTypeControlBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Collections; using System.Web.UI; namespace System.Web.Mvc { internal sealed class ViewTypeControlBuilder : ControlBuilder { private string _typeName; public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, IDictionary attribs) { base.Init(parser, parentBuilder, type, tagName, id, attribs); _typeName = (string)attribs["typename"]; } public override void ProcessGeneratedCode( CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod) { // Override the view's base type with the explicit base type derivedType.BaseTypes[0] = new CodeTypeReference(_typeName); } } } ================================================ FILE: src/System.Web.Mvc/ViewTypeParserFilter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.UI; namespace System.Web.Mvc { // This class is referenced dynamically by the web.config built by project templates. // Do not delete this class based on it not being statically referenced by product code. internal class ViewTypeParserFilter : PageParserFilter { private static Dictionary _directiveBaseTypeMappings = new Dictionary { { "page", typeof(ViewPage) }, { "control", typeof(ViewUserControl) }, { "master", typeof(ViewMasterPage) }, }; private string _inherits; public ViewTypeParserFilter() { } public override bool AllowCode { get { return true; } } public override int NumberOfControlsAllowed { get { return -1; } } public override int NumberOfDirectDependenciesAllowed { get { return -1; } } public override int TotalNumberOfDependenciesAllowed { get { return -1; } } public override void PreprocessDirective(string directiveName, IDictionary attributes) { base.PreprocessDirective(directiveName, attributes); Type baseType; if (_directiveBaseTypeMappings.TryGetValue(directiveName, out baseType)) { string inheritsAttribute = attributes["inherits"] as string; // Since the ASP.NET page parser doesn't understand native generic syntax, we // need to swap out whatever the user provided with the default base type for // the given directive (page vs. control vs. master). We stash the old value // and swap it back in inside the control builder. Our "is this generic?" // check here really only works for C# and VB.NET, since we're checking for // < or ( in the type name. // // We only change generic directives, because doing so breaks back-compat // for property setters on @Page, @Control, and @Master directives. The user // can work around this breaking behavior by using a non-generic inherits // directive, or by using the CLR syntax for generic type names. if (inheritsAttribute != null && inheritsAttribute.IndexOfAny(new[] { '<', '(' }) > 0) { attributes["inherits"] = baseType.FullName; _inherits = inheritsAttribute; } } } public override void ParseComplete(ControlBuilder rootBuilder) { base.ParseComplete(rootBuilder); IMvcControlBuilder builder = rootBuilder as IMvcControlBuilder; if (builder != null) { builder.Inherits = _inherits; } } // Everything else in this class is unrelated to our 'inherits' handling. // Since PageParserFilter blocks everything by default, we need to unblock it public override bool AllowBaseType(Type baseType) { return true; } public override bool AllowControl(Type controlType, ControlBuilder builder) { return true; } public override bool AllowVirtualReference(string referenceVirtualPath, VirtualReferenceType referenceType) { return true; } public override bool AllowServerSideInclude(string includeVirtualPath) { return true; } } } ================================================ FILE: src/System.Web.Mvc/ViewUserControl.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Web.Mvc.Properties; using System.Web.UI; namespace System.Web.Mvc { [FileLevelControlBuilder(typeof(ViewUserControlControlBuilder))] public class ViewUserControl : UserControl, IViewDataContainer { private AjaxHelper _ajaxHelper; private DynamicViewDataDictionary _dynamicViewData; private HtmlHelper _htmlHelper; private ViewContext _viewContext; private ViewDataDictionary _viewData; private string _viewDataKey; public AjaxHelper Ajax { get { if (_ajaxHelper == null) { _ajaxHelper = new AjaxHelper(ViewContext, this); } return _ajaxHelper; } } public HtmlHelper Html { get { if (_htmlHelper == null) { _htmlHelper = new HtmlHelper(ViewContext, this); } return _htmlHelper; } } public object Model { get { return ViewData.Model; } } public TempDataDictionary TempData { get { return ViewPage.TempData; } } public UrlHelper Url { get { return ViewPage.Url; } } public dynamic ViewBag { get { if (_dynamicViewData == null) { _dynamicViewData = new DynamicViewDataDictionary(() => ViewData); } return _dynamicViewData; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ViewContext ViewContext { get { return _viewContext ?? ViewPage.ViewContext; } set { _viewContext = value; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is the mechanism by which the ViewUserControl gets its ViewDataDictionary object.")] [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ViewDataDictionary ViewData { get { EnsureViewData(); return _viewData; } set { SetViewData(value); } } [DefaultValue("")] public string ViewDataKey { get { return _viewDataKey ?? String.Empty; } set { _viewDataKey = value; } } internal ViewPage ViewPage { get { ViewPage viewPage = Page as ViewPage; if (viewPage == null) { throw new InvalidOperationException(MvcResources.ViewUserControl_RequiresViewPage); } return viewPage; } } public HtmlTextWriter Writer { get { return ViewPage.Writer; } } protected virtual void SetViewData(ViewDataDictionary viewData) { _viewData = viewData; } protected void EnsureViewData() { if (_viewData != null) { return; } // Get the ViewData for this ViewUserControl, optionally using the specified ViewDataKey IViewDataContainer vdc = GetViewDataContainer(this); if (vdc == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ViewUserControl_RequiresViewDataProvider, AppRelativeVirtualPath)); } ViewDataDictionary myViewData = vdc.ViewData; // If we have a ViewDataKey, try to extract the ViewData from the dictionary, otherwise // return the container's ViewData. if (!String.IsNullOrEmpty(ViewDataKey)) { object target = myViewData.Eval(ViewDataKey); myViewData = target as ViewDataDictionary ?? new ViewDataDictionary(myViewData) { Model = target }; } SetViewData(myViewData); } private static IViewDataContainer GetViewDataContainer(Control control) { // Walk up the control hierarchy until we find someone that implements IViewDataContainer while (control != null) { control = control.Parent; IViewDataContainer vdc = control as IViewDataContainer; if (vdc != null) { return vdc; } } return null; } public virtual void RenderView(ViewContext viewContext) { using (ViewUserControlContainerPage containerPage = new ViewUserControlContainerPage(this)) { RenderViewAndRestoreContentType(containerPage, viewContext); } } internal static void RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext) { // We need to restore the Content-Type since Page.SetIntrinsics() will reset it. It's not possible // to work around the call to SetIntrinsics() since the control's render method requires the // containing page's Response property to be non-null, and SetIntrinsics() is the only way to set // this. string savedContentType = viewContext.HttpContext.Response.ContentType; containerPage.RenderView(viewContext); viewContext.HttpContext.Response.ContentType = savedContentType; } [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textWriter", Justification = "This method existed in MVC 1.0 and has been deprecated.")] [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method existed in MVC 1.0 and has been deprecated.")] [Obsolete("The TextWriter is now provided by the ViewContext object passed to the RenderView method.", true /* error */)] public void SetTextWriter(TextWriter textWriter) { // this is now a no-op } private sealed class ViewUserControlContainerPage : ViewPage { private readonly ViewUserControl _userControl; public ViewUserControlContainerPage(ViewUserControl userControl) { _userControl = userControl; } public override void ProcessRequest(HttpContext context) { _userControl.ID = NextId(); Controls.Add(_userControl); base.ProcessRequest(context); } } } } ================================================ FILE: src/System.Web.Mvc/ViewUserControlControlBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.UI; namespace System.Web.Mvc { internal sealed class ViewUserControlControlBuilder : FileLevelUserControlBuilder, IMvcControlBuilder { public string Inherits { get; set; } public override void ProcessGeneratedCode(CodeCompileUnit codeCompileUnit, CodeTypeDeclaration baseType, CodeTypeDeclaration derivedType, CodeMemberMethod buildMethod, CodeMemberMethod dataBindingMethod) { if (!String.IsNullOrWhiteSpace(Inherits)) { derivedType.BaseTypes[0] = new CodeTypeReference(Inherits); } } } } ================================================ FILE: src/System.Web.Mvc/ViewUserControlOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace System.Web.Mvc { public class ViewUserControl : ViewUserControl { private AjaxHelper _ajaxHelper; private HtmlHelper _htmlHelper; private ViewDataDictionary _viewData; public new AjaxHelper Ajax { get { if (_ajaxHelper == null) { _ajaxHelper = new AjaxHelper(ViewContext, this); } return _ajaxHelper; } } public new HtmlHelper Html { get { if (_htmlHelper == null) { _htmlHelper = new HtmlHelper(ViewContext, this); } return _htmlHelper; } } public new TModel Model { get { return ViewData.Model; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is settable for unit testing purposes")] public new ViewDataDictionary ViewData { get { EnsureViewData(); return _viewData; } set { SetViewData(value); } } protected override void SetViewData(ViewDataDictionary viewData) { _viewData = new ViewDataDictionary(viewData); base.SetViewData(_viewData); } } } ================================================ FILE: src/System.Web.Mvc/VirtualPathProviderViewEngine.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Web.Hosting; using System.Web.Mvc.Properties; using System.Web.WebPages; namespace System.Web.Mvc { public abstract class VirtualPathProviderViewEngine : IViewEngine { // format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:" private const string CacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:"; private const string CacheKeyPrefixMaster = "Master"; private const string CacheKeyPrefixPartial = "Partial"; private const string CacheKeyPrefixView = "View"; private static readonly string[] _emptyLocations = new string[0]; private DisplayModeProvider _displayModeProvider; private Func _vppFunc = () => HostingEnvironment.VirtualPathProvider; internal Func GetExtensionThunk = VirtualPathUtility.GetExtension; private IViewLocationCache _viewLocationCache; [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] AreaMasterLocationFormats { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] AreaPartialViewLocationFormats { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] AreaViewLocationFormats { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] FileExtensions { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] MasterLocationFormats { get; set; } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] PartialViewLocationFormats { get; set; } // Neither DefaultViewLocationCache.Null nor a DefaultViewLocationCache instance maintain internal state. Fine // if multiple threads race to initialize _viewLocationCache. public IViewLocationCache ViewLocationCache { get { if (_viewLocationCache == null) { if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled) { _viewLocationCache = DefaultViewLocationCache.Null; } else { _viewLocationCache = new DefaultViewLocationCache(); } } return _viewLocationCache; } set { if (value == null) { throw Error.ArgumentNull("value"); } _viewLocationCache = value; } } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "This is a shipped API")] public string[] ViewLocationFormats { get; set; } // Likely exists for testing only protected VirtualPathProvider VirtualPathProvider { get { return _vppFunc(); } set { if (value == null) { throw Error.ArgumentNull("value"); } _vppFunc = () => value; } } // Provided for testing only; setter used in BuildManagerViewEngine but only for test scenarios internal Func VirtualPathProviderFunc { get { return _vppFunc; } set { if (value == null) { throw Error.ArgumentNull("value"); } _vppFunc = value; } } protected internal DisplayModeProvider DisplayModeProvider { get { return _displayModeProvider ?? DisplayModeProvider.Instance; } set { _displayModeProvider = value; } } internal virtual string CreateCacheKey(string prefix, string name, string controllerName, string areaName) { return String.Format(CultureInfo.InvariantCulture, CacheKeyFormat, GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName); } internal static string AppendDisplayModeToCacheKey(string cacheKey, string displayMode) { // key format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:" // so append "{displayMode}:" to the key return cacheKey + displayMode + ":"; } protected abstract IView CreatePartialView(ControllerContext controllerContext, string partialPath); protected abstract IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath); protected virtual bool FileExists(ControllerContext controllerContext, string virtualPath) { return VirtualPathProvider.FileExists(virtualPath); } public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(partialViewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "partialViewName"); } string[] searched; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string partialPath = GetPath(controllerContext, PartialViewLocationFormats, AreaPartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, controllerName, CacheKeyPrefixPartial, useCache, out searched); if (String.IsNullOrEmpty(partialPath)) { return new ViewEngineResult(searched); } return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this); } public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(viewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName"); } string[] viewLocationsSearched; string[] masterLocationsSearched; string controllerName = controllerContext.RouteData.GetRequiredString("controller"); string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched); string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched); if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) { return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); } return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); } private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) { searchedLocations = _emptyLocations; if (String.IsNullOrEmpty(name)) { return String.Empty; } string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData); bool usingAreas = !String.IsNullOrEmpty(areaName); List viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null); if (viewLocations.Count == 0) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName)); } bool nameRepresentsPath = IsSpecificPath(name); string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName); if (useCache) { // Only look at cached display modes that can handle the context. IEnumerable possibleDisplayModes = DisplayModeProvider.GetAvailableDisplayModesForContext(controllerContext.HttpContext, controllerContext.DisplayMode); foreach (IDisplayMode displayMode in possibleDisplayModes) { string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId)); if (cachedLocation == null) { // If any matching display mode location is not in the cache, fall back to the uncached behavior, which will repopulate all of our caches. return null; } // A non-empty cachedLocation indicates that we have a matching file on disk. Return that result. if (cachedLocation.Length > 0) { if (controllerContext.DisplayMode == null) { controllerContext.DisplayMode = displayMode; } return cachedLocation; } // An empty cachedLocation value indicates that we don't have a matching file on disk. Keep going down the list of possible display modes. } // GetPath is called again without using the cache. return null; } else { return nameRepresentsPath ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) : GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations); } } private string GetPathFromGeneralName(ControllerContext controllerContext, List locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations) { string result = String.Empty; searchedLocations = new string[locations.Count]; for (int i = 0; i < locations.Count; i++) { ViewLocation location = locations[i]; string virtualPath = location.Format(name, controllerName, areaName); DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode); if (virtualPathDisplayInfo != null) { string resolvedVirtualPath = virtualPathDisplayInfo.FilePath; searchedLocations = _emptyLocations; result = resolvedVirtualPath; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, virtualPathDisplayInfo.DisplayMode.DisplayModeId), result); if (controllerContext.DisplayMode == null) { controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode; } // Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish // in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string). IEnumerable allDisplayModes = DisplayModeProvider.Modes; foreach (IDisplayMode displayMode in allDisplayModes) { if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId) { DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path)); string cacheValue = String.Empty; if (displayInfoToCache != null && displayInfoToCache.FilePath != null) { cacheValue = displayInfoToCache.FilePath; } ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId), cacheValue); } } break; } searchedLocations[i] = virtualPath; } return result; } private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) { string result = name; if (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) { result = String.Empty; searchedLocations = new[] { name }; } ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); return result; } private bool FilePathIsSupported(string virtualPath) { if (FileExtensions == null) { // legacy behavior for custom ViewEngine that might not set the FileExtensions property return true; } else { // get rid of the '.' because the FileExtensions property expects extensions withouth a dot. string extension = GetExtensionThunk(virtualPath).TrimStart('.'); return FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } } private static List GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats) { List allLocations = new List(); if (areaViewLocationFormats != null) { foreach (string areaViewLocationFormat in areaViewLocationFormats) { allLocations.Add(new AreaAwareViewLocation(areaViewLocationFormat)); } } if (viewLocationFormats != null) { foreach (string viewLocationFormat in viewLocationFormats) { allLocations.Add(new ViewLocation(viewLocationFormat)); } } return allLocations; } private static bool IsSpecificPath(string name) { char c = name[0]; return (c == '~' || c == '/'); } public virtual void ReleaseView(ControllerContext controllerContext, IView view) { IDisposable disposable = view as IDisposable; if (disposable != null) { disposable.Dispose(); } } private class AreaAwareViewLocation : ViewLocation { public AreaAwareViewLocation(string virtualPathFormatString) : base(virtualPathFormatString) { } public override string Format(string viewName, string controllerName, string areaName) { return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName, areaName); } } private class ViewLocation { protected string _virtualPathFormatString; public ViewLocation(string virtualPathFormatString) { _virtualPathFormatString = virtualPathFormatString; } public virtual string Format(string viewName, string controllerName, string areaName) { return String.Format(CultureInfo.InvariantCulture, _virtualPathFormatString, viewName, controllerName); } } } } ================================================ FILE: src/System.Web.Mvc/WebFormView.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.IO; using System.Web.Mvc.Properties; namespace System.Web.Mvc { public class WebFormView : BuildManagerCompiledView { public WebFormView(ControllerContext controllerContext, string viewPath) : this(controllerContext, viewPath, null, null) { } public WebFormView(ControllerContext controllerContext, string viewPath, string masterPath) : this(controllerContext, viewPath, masterPath, null) { } public WebFormView(ControllerContext controllerContext, string viewPath, string masterPath, IViewPageActivator viewPageActivator) : base(controllerContext, viewPath, viewPageActivator) { MasterPath = masterPath ?? String.Empty; } public string MasterPath { get; private set; } protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance) { ViewPage viewPage = instance as ViewPage; if (viewPage != null) { RenderViewPage(viewContext, viewPage); return; } ViewUserControl viewUserControl = instance as ViewUserControl; if (viewUserControl != null) { RenderViewUserControl(viewContext, viewUserControl); return; } throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.WebFormViewEngine_WrongViewBase, ViewPath)); } private void RenderViewPage(ViewContext context, ViewPage page) { if (!String.IsNullOrEmpty(MasterPath)) { page.MasterLocation = MasterPath; } page.ViewData = context.ViewData; page.RenderView(context); } private void RenderViewUserControl(ViewContext context, ViewUserControl control) { if (!String.IsNullOrEmpty(MasterPath)) { throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster); } control.ViewData = context.ViewData; control.RenderView(context); } } } ================================================ FILE: src/System.Web.Mvc/WebFormViewEngine.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Mvc { public class WebFormViewEngine : BuildManagerViewEngine { public WebFormViewEngine() : this(null) { } public WebFormViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator) { MasterLocationFormats = new[] { "~/Views/{1}/{0}.master", "~/Views/Shared/{0}.master" }; AreaMasterLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.master", "~/Areas/{2}/Views/Shared/{0}.master", }; ViewLocationFormats = new[] { "~/Views/{1}/{0}.aspx", "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.aspx", "~/Views/Shared/{0}.ascx" }; AreaViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.aspx", "~/Areas/{2}/Views/{1}/{0}.ascx", "~/Areas/{2}/Views/Shared/{0}.aspx", "~/Areas/{2}/Views/Shared/{0}.ascx", }; PartialViewLocationFormats = ViewLocationFormats; AreaPartialViewLocationFormats = AreaViewLocationFormats; FileExtensions = new[] { "aspx", "ascx", "master", }; } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return new WebFormView(controllerContext, partialPath, null, ViewPageActivator); } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return new WebFormView(controllerContext, viewPath, masterPath, ViewPageActivator); } } } ================================================ FILE: src/System.Web.Mvc/WebViewPage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Web.Mvc.Properties; using System.Web.WebPages; namespace System.Web.Mvc { public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild { private ViewDataDictionary _viewData; private DynamicViewDataDictionary _dynamicViewData; private HttpContextBase _context; private HtmlHelper _html; private AjaxHelper _ajax; public override HttpContextBase Context { // REVIEW why are we forced to override this? get { return _context ?? ViewContext.HttpContext; } set { _context = value; } } public HtmlHelper Html { get { if (_html == null && ViewContext != null) { _html = new HtmlHelper(ViewContext, this); } return _html; } set { _html = value; } } public AjaxHelper Ajax { get { if (_ajax == null && ViewContext != null) { _ajax = new AjaxHelper(ViewContext, this); } return _ajax; } set { _ajax = value; } } public object Model { get { return ViewData.Model; } } internal string OverridenLayoutPath { get; set; } public TempDataDictionary TempData { get { return ViewContext.TempData; } } public UrlHelper Url { get; set; } public dynamic ViewBag { get { if (_dynamicViewData == null) { _dynamicViewData = new DynamicViewDataDictionary(() => ViewData); } return _dynamicViewData; } } public ViewContext ViewContext { get; set; } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is the mechanism by which the ViewPage gets its ViewDataDictionary object.")] public ViewDataDictionary ViewData { get { if (_viewData == null) { SetViewData(new ViewDataDictionary()); } return _viewData; } set { SetViewData(value); } } protected override void ConfigurePage(WebPageBase parentPage) { var baseViewPage = parentPage as WebViewPage; if (baseViewPage == null) { // TODO : review if this check is even necessary. // When this method is called by the framework parentPage should already be an instance of WebViewPage // Need to review what happens if this method gets called in Plan9 pointing at an MVC view throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_WrongViewBase, parentPage.VirtualPath)); } // Set ViewContext and ViewData here so that the layout page inherits ViewData from the main page ViewContext = baseViewPage.ViewContext; ViewData = baseViewPage.ViewData; InitHelpers(); } public override void ExecutePageHierarchy() { // Change the Writer so that things like Html.BeginForm work correctly TextWriter oldWriter = ViewContext.Writer; ViewContext.Writer = Output; base.ExecutePageHierarchy(); // Overwrite LayoutPage so that returning a view with a custom master page works. if (!String.IsNullOrEmpty(OverridenLayoutPath)) { Layout = OverridenLayoutPath; } // Restore the old View Context Writer ViewContext.Writer = oldWriter; } public virtual void InitHelpers() { // Html and Ajax helpers are lazily initialized since they are not directly visible to a Razor page. // In order to ensure back-compat, in the event that this instance gets re-used, we'll reset these // properties so they get reinitialized the very next time they get accessed. Html = null; Ajax = null; Url = new UrlHelper(ViewContext.RequestContext); } protected virtual void SetViewData(ViewDataDictionary viewData) { _viewData = viewData; } } } ================================================ FILE: src/System.Web.Mvc/WebViewPageOfTModel.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace System.Web.Mvc { public abstract class WebViewPage : WebViewPage { private ViewDataDictionary _viewData; public new AjaxHelper Ajax { get; set; } public new HtmlHelper Html { get; set; } public new TModel Model { get { return ViewData.Model; } } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "This is the mechanism by which the ViewPage gets its ViewDataDictionary object.")] public new ViewDataDictionary ViewData { get { if (_viewData == null) { SetViewData(new ViewDataDictionary()); } return _viewData; } set { SetViewData(value); } } public override void InitHelpers() { base.InitHelpers(); Ajax = new AjaxHelper(ViewContext, this); Html = new HtmlHelper(ViewContext, this); } protected override void SetViewData(ViewDataDictionary viewData) { _viewData = new ViewDataDictionary(viewData); base.SetViewData(_viewData); } } } ================================================ FILE: src/System.Web.Mvc/packages.config ================================================  ================================================ FILE: src/System.Web.Razor/CSharpRazorCodeLanguage.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Generator; using System.Web.Razor.Parser; using Microsoft.CSharp; namespace System.Web.Razor { /// /// Defines the C# Code Language for Razor /// public class CSharpRazorCodeLanguage : RazorCodeLanguage { private const string CSharpLanguageName = "csharp"; /// /// Returns the name of the language: "csharp" /// public override string LanguageName { get { return CSharpLanguageName; } } /// /// Returns the type of the CodeDOM provider for this language /// public override Type CodeDomProviderType { get { return typeof(CSharpCodeProvider); } } /// /// Constructs a new instance of the code parser for this language /// public override ParserBase CreateCodeParser() { return new CSharpCodeParser(); } /// /// Constructs a new instance of the code generator for this language with the specified settings /// public override RazorCodeGenerator CreateCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host) { return new CSharpRazorCodeGenerator(className, rootNamespaceName, sourceFileName, host); } } } ================================================ FILE: src/System.Web.Razor/DocumentParseCompleteEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Text; namespace System.Web.Razor { /// /// Arguments for the DocumentParseComplete event in RazorEditorParser /// public class DocumentParseCompleteEventArgs : EventArgs { /// /// Indicates if the tree structure has actually changed since the previous reparse. /// public bool TreeStructureChanged { get; set; } /// /// The results of the code generation and parsing /// public GeneratorResults GeneratorResults { get; set; } /// /// The TextChange which triggered the reparse /// public TextChange SourceChange { get; set; } } } ================================================ FILE: src/System.Web.Razor/Editor/AutoCompleteEditHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Editor; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Parser.SyntaxTree { public class AutoCompleteEditHandler : SpanEditHandler { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public AutoCompleteEditHandler(Func> tokenizer) : base(tokenizer) { } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public AutoCompleteEditHandler(Func> tokenizer, AcceptedCharacters accepted) : base(tokenizer, accepted) { } public bool AutoCompleteAtEndOfSpan { get; set; } public string AutoCompleteString { get; set; } protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (((AutoCompleteAtEndOfSpan && IsAtEndOfSpan(target, normalizedChange)) || IsAtEndOfFirstLine(target, normalizedChange)) && normalizedChange.IsInsert && ParserHelpers.IsNewLine(normalizedChange.NewText) && AutoCompleteString != null) { return PartialParseResult.Rejected | PartialParseResult.AutoCompleteBlock; } return PartialParseResult.Rejected; } public override string ToString() { return base.ToString() + ",AutoComplete:[" + (AutoCompleteString ?? "") + "]" + (AutoCompleteAtEndOfSpan ? ";AtEnd" : ";AtEOL"); } public override bool Equals(object obj) { AutoCompleteEditHandler other = obj as AutoCompleteEditHandler; return base.Equals(obj) && other != null && String.Equals(other.AutoCompleteString, AutoCompleteString, StringComparison.Ordinal) && AutoCompleteAtEndOfSpan == other.AutoCompleteAtEndOfSpan; } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(base.GetHashCode()) .Add(AutoCompleteString) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Editor/BackgroundParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Utils; namespace System.Web.Razor.Editor { internal class BackgroundParser : IDisposable { private MainThreadState _main; private BackgroundThread _bg; public BackgroundParser(RazorEngineHost host, string fileName) { _main = new MainThreadState(fileName); _bg = new BackgroundThread(_main, host, fileName); _main.ResultsReady += (sender, args) => OnResultsReady(args); } /// /// Fired on the main thread. /// public event EventHandler ResultsReady; public bool IsIdle { get { return _main.IsIdle; } } public void Start() { _bg.Start(); } public void Cancel() { _main.Cancel(); } public void QueueChange(TextChange change) { _main.QueueChange(change); } [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "_main", Justification = "MainThreadState is disposed when the background thread shuts down")] public void Dispose() { _main.Cancel(); } public IDisposable SynchronizeMainThreadState() { return _main.Lock(); } protected virtual void OnResultsReady(DocumentParseCompleteEventArgs args) { var handler = ResultsReady; if (handler != null) { handler(this, args); } } internal static bool TreesAreDifferent(Block leftTree, Block rightTree, IEnumerable changes) { return TreesAreDifferent(leftTree, rightTree, changes, CancellationToken.None); } internal static bool TreesAreDifferent(Block leftTree, Block rightTree, IEnumerable changes, CancellationToken cancelToken) { // Apply all the pending changes to the original tree // PERF: If this becomes a bottleneck, we can probably do it the other way around, // i.e. visit the tree and find applicable changes for each node. foreach (TextChange change in changes) { cancelToken.ThrowIfCancellationRequested(); Span changeOwner = leftTree.LocateOwner(change); // Apply the change to the tree if (changeOwner == null) { return true; } EditResult result = changeOwner.EditHandler.ApplyChange(changeOwner, change, force: true); changeOwner.ReplaceWith(result.EditedSpan); } // Now compare the trees bool treesDifferent = !leftTree.EquivalentTo(rightTree); return treesDifferent; } private abstract class ThreadStateBase { #if DEBUG private int _id = -1; #endif protected ThreadStateBase() { } [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method is only empty in Release builds. In Debug builds it contains references to instance variables")] [Conditional("DEBUG")] protected void SetThreadId(int id) { #if DEBUG _id = id; #endif } [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method is only empty in Release builds. In Debug builds it contains references to instance variables")] [Conditional("DEBUG")] protected void EnsureOnThread() { #if DEBUG Debug.Assert(_id != -1, "SetThreadId was never called!"); Debug.Assert(Thread.CurrentThread.ManagedThreadId == _id, "Called from an unexpected thread!"); #endif } [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This method is only empty in Release builds. In Debug builds it contains references to instance variables")] [Conditional("DEBUG")] protected void EnsureNotOnThread() { #if DEBUG Debug.Assert(_id != -1, "SetThreadId was never called!"); Debug.Assert(Thread.CurrentThread.ManagedThreadId != _id, "Called from an unexpected thread!"); #endif } } private class MainThreadState : ThreadStateBase, IDisposable { private CancellationTokenSource _cancelSource = new CancellationTokenSource(); private ManualResetEventSlim _hasParcel = new ManualResetEventSlim(false); private CancellationTokenSource _currentParcelCancelSource; [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Field is used in debug code and may be used later")] private string _fileName; private object _stateLock = new object(); private IList _changes = new List(); public MainThreadState(string fileName) { _fileName = fileName; SetThreadId(Thread.CurrentThread.ManagedThreadId); } public event EventHandler ResultsReady; public CancellationToken CancelToken { get { return _cancelSource.Token; } } public bool IsIdle { get { lock (_stateLock) { return _currentParcelCancelSource == null; } } } public void Cancel() { EnsureOnThread(); _cancelSource.Cancel(); } public IDisposable Lock() { Monitor.Enter(_stateLock); return new DisposableAction(() => Monitor.Exit(_stateLock)); } public void QueueChange(TextChange change) { RazorEditorTrace.TraceLine(RazorResources.Trace_QueuingParse, Path.GetFileName(_fileName), change); EnsureOnThread(); lock (_stateLock) { // CurrentParcel token source is not null ==> There's a parse underway if (_currentParcelCancelSource != null) { _currentParcelCancelSource.Cancel(); } _changes.Add(change); _hasParcel.Set(); } } public WorkParcel GetParcel() { EnsureNotOnThread(); // Only the background thread can get a parcel _hasParcel.Wait(_cancelSource.Token); _hasParcel.Reset(); lock (_stateLock) { // Create a cancellation source for this parcel _currentParcelCancelSource = new CancellationTokenSource(); var changes = _changes; _changes = new List(); return new WorkParcel(changes, _currentParcelCancelSource.Token); } } public void ReturnParcel(DocumentParseCompleteEventArgs args) { lock (_stateLock) { // Clear the current parcel cancellation source if (_currentParcelCancelSource != null) { _currentParcelCancelSource.Dispose(); _currentParcelCancelSource = null; } // If there are things waiting to be parsed, just don't fire the event because we're already out of date if (_changes.Any()) { return; } } var handler = ResultsReady; if (handler != null) { handler(this, args); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (_currentParcelCancelSource != null) { _currentParcelCancelSource.Dispose(); _currentParcelCancelSource = null; } _cancelSource.Dispose(); _hasParcel.Dispose(); } } } private class BackgroundThread : ThreadStateBase { private MainThreadState _main; private Thread _backgroundThread; private CancellationToken _shutdownToken; private RazorEngineHost _host; private string _fileName; private Block _currentParseTree; private IList _previouslyDiscarded = new List(); public BackgroundThread(MainThreadState main, RazorEngineHost host, string fileName) { // Run on MAIN thread! _main = main; _backgroundThread = new Thread(WorkerLoop); _shutdownToken = _main.CancelToken; _host = host; _fileName = fileName; SetThreadId(_backgroundThread.ManagedThreadId); } // **** ANY THREAD **** public void Start() { _backgroundThread.Start(); } // **** BACKGROUND THREAD **** private void WorkerLoop() { long? elapsedMs = null; string fileNameOnly = Path.GetFileName(_fileName); #if EDITOR_TRACING Stopwatch sw = new Stopwatch(); #endif try { RazorEditorTrace.TraceLine(RazorResources.Trace_BackgroundThreadStart, fileNameOnly); EnsureOnThread(); while (!_shutdownToken.IsCancellationRequested) { // Grab the parcel of work to do WorkParcel parcel = _main.GetParcel(); if (parcel.Changes.Any()) { RazorEditorTrace.TraceLine(RazorResources.Trace_ChangesArrived, fileNameOnly, parcel.Changes.Count); try { DocumentParseCompleteEventArgs args = null; using (var linkedCancel = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken, parcel.CancelToken)) { if (!linkedCancel.IsCancellationRequested) { // Collect ALL changes #if EDITOR_TRACING if (_previouslyDiscarded != null && _previouslyDiscarded.Any()) { RazorEditorTrace.TraceLine(RazorResources.Trace_CollectedDiscardedChanges, fileNameOnly, _previouslyDiscarded.Count); } #endif List allChanges; if (_previouslyDiscarded != null) { allChanges = Enumerable.Concat(_previouslyDiscarded, parcel.Changes).ToList(); } else { allChanges = parcel.Changes.ToList(); } TextChange finalChange = allChanges.Last(); #if EDITOR_TRACING sw.Start(); #endif GeneratorResults results = ParseChange(finalChange.NewBuffer, linkedCancel.Token); #if EDITOR_TRACING sw.Stop(); elapsedMs = sw.ElapsedMilliseconds; sw.Reset(); #endif RazorEditorTrace.TraceLine( RazorResources.Trace_ParseComplete, fileNameOnly, elapsedMs.HasValue ? elapsedMs.Value.ToString(CultureInfo.InvariantCulture) : "?"); if (results != null && !linkedCancel.IsCancellationRequested) { // Clear discarded changes list _previouslyDiscarded = null; // Take the current tree and check for differences #if EDITOR_TRACING sw.Start(); #endif bool treeStructureChanged = _currentParseTree == null || TreesAreDifferent(_currentParseTree, results.Document, allChanges, parcel.CancelToken); #if EDITOR_TRACING sw.Stop(); elapsedMs = sw.ElapsedMilliseconds; sw.Reset(); #endif _currentParseTree = results.Document; RazorEditorTrace.TraceLine(RazorResources.Trace_TreesCompared, fileNameOnly, elapsedMs.HasValue ? elapsedMs.Value.ToString(CultureInfo.InvariantCulture) : "?", treeStructureChanged); // Build Arguments args = new DocumentParseCompleteEventArgs() { GeneratorResults = results, SourceChange = finalChange, TreeStructureChanged = treeStructureChanged }; } else { // Parse completed but we were cancelled in the mean time. Add these to the discarded changes set RazorEditorTrace.TraceLine(RazorResources.Trace_ChangesDiscarded, fileNameOnly, allChanges.Count); _previouslyDiscarded = allChanges; } #if CHECK_TREE if (args != null) { // Rewind the buffer and sanity check the line mappings finalChange.NewBuffer.Position = 0; int lineCount = finalChange.NewBuffer.ReadToEnd().Split(new string[] { Environment.NewLine, "\r", "\n" }, StringSplitOptions.None).Count(); Debug.Assert( !args.GeneratorResults.DesignTimeLineMappings.Any(pair => pair.Value.StartLine > lineCount), "Found a design-time line mapping referring to a line outside the source file!"); Debug.Assert( !args.GeneratorResults.Document.Flatten().Any(span => span.Start.LineIndex > lineCount), "Found a span with a line number outside the source file"); Debug.Assert( !args.GeneratorResults.Document.Flatten().Any(span => span.Start.AbsoluteIndex > parcel.NewBuffer.Length), "Found a span with an absolute offset outside the source file"); } #endif } } if (args != null) { _main.ReturnParcel(args); } } catch (OperationCanceledException) { } } else { RazorEditorTrace.TraceLine(RazorResources.Trace_NoChangesArrived, fileNameOnly, parcel.Changes.Count); Thread.Yield(); } } } catch (OperationCanceledException) { // Do nothing. Just shut down. } finally { RazorEditorTrace.TraceLine(RazorResources.Trace_BackgroundThreadShutdown, fileNameOnly); // Clean up main thread resources _main.Dispose(); } } private GeneratorResults ParseChange(ITextBuffer buffer, CancellationToken token) { EnsureOnThread(); // Create a template engine RazorTemplateEngine engine = new RazorTemplateEngine(_host); // Seek the buffer to the beginning buffer.Position = 0; try { return engine.GenerateCode( input: buffer, className: null, rootNamespace: null, sourceFileName: _fileName, cancelToken: token); } catch (OperationCanceledException) { return null; } } } private class WorkParcel { public WorkParcel(IList changes, CancellationToken cancelToken) { Changes = changes; CancelToken = cancelToken; } public CancellationToken CancelToken { get; private set; } public IList Changes { get; private set; } } } } ================================================ FILE: src/System.Web.Razor/Editor/EditResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Editor { public class EditResult { public EditResult(PartialParseResult result, SpanBuilder editedSpan) { Result = result; EditedSpan = editedSpan; } public PartialParseResult Result { get; set; } public SpanBuilder EditedSpan { get; set; } } } ================================================ FILE: src/System.Web.Razor/Editor/EditorHints.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Editor { /// /// Used within . /// [Flags] public enum EditorHints { /// /// The default (Markup or Code) editor behavior for Statement completion should be used. /// Editors can always use the default behavior, even if the span is labeled with a different CompletionType. /// None = 0, // 0000 0000 /// /// Indicates that Virtual Path completion should be used for this span if the editor supports it. /// Editors need not support this mode of completion, and will use the default () behavior /// if they do not support it. /// VirtualPath = 1, // 0000 0001 /// /// Indicates that this span's content contains the path to the layout page for this document. /// LayoutPage = 2, // 0000 0010 } } ================================================ FILE: src/System.Web.Razor/Editor/ImplicitExpressionEditHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Editor { public class ImplicitExpressionEditHandler : SpanEditHandler { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public ImplicitExpressionEditHandler(Func> tokenizer, ISet keywords, bool acceptTrailingDot) : base(tokenizer) { Initialize(keywords, acceptTrailingDot); } public bool AcceptTrailingDot { get; private set; } public ISet Keywords { get; private set; } public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{0};ImplicitExpression[{1}];K{2}", base.ToString(), AcceptTrailingDot ? "ATD" : "RTD", Keywords.Count); } public override bool Equals(object obj) { ImplicitExpressionEditHandler other = obj as ImplicitExpressionEditHandler; return other != null && base.Equals(other) && Keywords.SetEquals(other.Keywords) && AcceptTrailingDot == other.AcceptTrailingDot; } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(base.GetHashCode()) .Add(AcceptTrailingDot) .Add(Keywords) .CombinedHash; } protected override PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { if (AcceptedCharacters == AcceptedCharacters.Any) { return PartialParseResult.Rejected; } // In some editors intellisense insertions are handled as "dotless commits". If an intellisense selection is confirmed // via something like '.' a dotless commit will append a '.' and then insert the remaining intellisense selection prior // to the appended '.'. This 'if' statement attempts to accept the intermediate steps of a dotless commit via // intellisense. It will accept two cases: // 1. '@foo.' -> '@foobaz.'. // 2. '@foobaz..' -> '@foobaz.bar.'. Includes Sub-cases '@foobaz()..' -> '@foobaz().bar.' etc. // The key distinction being the double '.' in the second case. if (IsDotlessCommitInsertion(target, normalizedChange)) { return HandleDotlessCommitInsertion(target); } if (IsAcceptableIdentifierReplacement(target, normalizedChange)) { return TryAcceptChange(target, normalizedChange); } if (IsAcceptableReplace(target, normalizedChange)) { return HandleReplacement(target, normalizedChange); } int changeRelativePosition = normalizedChange.OldPosition - target.Start.AbsoluteIndex; // Get the edit context char? lastChar = null; if (changeRelativePosition > 0 && target.Content.Length > 0) { lastChar = target.Content[changeRelativePosition - 1]; } // Don't support 0->1 length edits if (lastChar == null) { return PartialParseResult.Rejected; } // Accepts cases when insertions are made at the end of a span or '.' is inserted within a span. if (IsAcceptableInsertion(target, normalizedChange)) { // Handle the insertion return HandleInsertion(target, lastChar.Value, normalizedChange); } if (IsAcceptableDeletion(target, normalizedChange)) { return HandleDeletion(target, lastChar.Value, normalizedChange); } return PartialParseResult.Rejected; } private void Initialize(ISet keywords, bool acceptTrailingDot) { Keywords = keywords ?? new HashSet(); AcceptTrailingDot = acceptTrailingDot; } // A dotless commit is the process of inserting a '.' with an intellisense selection. private static bool IsDotlessCommitInsertion(Span target, TextChange change) { return IsNewDotlessCommitInsertion(target, change) || IsSecondaryDotlessCommitInsertion(target, change); } // Completing 'DateTime' in intellisense with a '.' could result in: '@DateT' -> '@DateT.' -> '@DateTime.' which is accepted. private static bool IsNewDotlessCommitInsertion(Span target, TextChange change) { return !IsAtEndOfSpan(target, change) && change.NewPosition > 0 && change.NewLength > 0 && target.Content.Last() == '.' && ParserHelpers.IsIdentifier(change.NewText, requireIdentifierStart: false) && (change.OldLength == 0 || ParserHelpers.IsIdentifier(change.OldText, requireIdentifierStart: false)); } // Once a dotless commit has been performed you then have something like '@DateTime.'. This scenario is used to detect the // situation when you try to perform another dotless commit resulting in a textchange with '..'. Completing 'DateTime.Now' // in intellisense with a '.' could result in: '@DateTime.' -> '@DateTime..' -> '@DateTime.Now.' which is accepted. private static bool IsSecondaryDotlessCommitInsertion(Span target, TextChange change) { // Do not need to worry about other punctuation, just looking for double '.' (after change) return change.NewLength == 1 && !String.IsNullOrEmpty(target.Content) && target.Content.Last() == '.' && change.NewText == "." && change.OldLength == 0; } private bool IsAcceptableIdentifierReplacement(Span target, TextChange change) { if (!change.IsReplace) { return false; } foreach (ISymbol isymbol in target.Symbols) { CSharpSymbol symbol = isymbol as CSharpSymbol; if (symbol == null) { break; } int symbolStartIndex = target.Start.AbsoluteIndex + symbol.Start.AbsoluteIndex; int symbolEndIndex = symbolStartIndex + symbol.Content.Length; // We're looking for the first symbol that contains the TextChange. if (symbolEndIndex > change.OldPosition) { if (symbolEndIndex >= change.OldPosition + change.OldLength && symbol.Type == CSharpSymbolType.Identifier) { // The symbol we're changing happens to be an identifier. Need to check if its transformed state is also one. // We do this transformation logic to capture the case that the new text change happens to not be an identifier; // i.e. "5". Alone, it's numeric, within an identifier it's classified as identifier. string transformedContent = change.ApplyChange(symbol.Content, symbolStartIndex); IEnumerable newSymbols = Tokenizer(transformedContent); if (newSymbols.Count() != 1) { // The transformed content resulted in more than one symbol; we can only replace a single identifier with // another single identifier. break; } CSharpSymbol newSymbol = (CSharpSymbol)newSymbols.First(); if (newSymbol.Type == CSharpSymbolType.Identifier) { return true; } } // Change is touching a non-identifier symbol or spans multiple symbols. break; } } return false; } private static bool IsAcceptableReplace(Span target, TextChange change) { return IsEndReplace(target, change) || (change.IsReplace && RemainingIsWhitespace(target, change)); } private static bool IsAcceptableDeletion(Span target, TextChange change) { return IsEndDeletion(target, change) || (change.IsDelete && RemainingIsWhitespace(target, change)); } // Acceptable insertions can occur at the end of a span or when a '.' is inserted within a span. private static bool IsAcceptableInsertion(Span target, TextChange change) { return change.IsInsert && (IsAcceptableEndInsertion(target, change) || IsAcceptableInnerInsertion(target, change)); } // Accepts character insertions at the end of spans. AKA: '@foo' -> '@fooo' or '@foo' -> '@foo ' etc. private static bool IsAcceptableEndInsertion(Span target, TextChange change) { Debug.Assert(change.IsInsert); return IsAtEndOfSpan(target, change) || RemainingIsWhitespace(target, change); } // Accepts '.' insertions in the middle of spans. Ex: '@foo.baz.bar' -> '@foo..baz.bar' // This is meant to allow intellisense when editing a span. [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "target", Justification = "The 'target' parameter is used in Debug to validate that the function is called in the correct context.")] private static bool IsAcceptableInnerInsertion(Span target, TextChange change) { Debug.Assert(change.IsInsert); // Ensure that we're actually inserting in the middle of a span and not at the end. // This case will fail if the IsAcceptableEndInsertion does not capture an end insertion correctly. Debug.Assert(!IsAtEndOfSpan(target, change)); return change.NewPosition > 0 && change.NewText == "."; } private static bool RemainingIsWhitespace(Span target, TextChange change) { int offset = (change.OldPosition - target.Start.AbsoluteIndex) + change.OldLength; return String.IsNullOrWhiteSpace(target.Content.Substring(offset)); } private PartialParseResult HandleDotlessCommitInsertion(Span target) { PartialParseResult result = PartialParseResult.Accepted; if (!AcceptTrailingDot && target.Content.LastOrDefault() == '.') { result |= PartialParseResult.Provisional; } return result; } private PartialParseResult HandleReplacement(Span target, TextChange change) { // Special Case for IntelliSense commits. // When IntelliSense commits, we get two changes (for example user typed "Date", then committed "DateTime" by pressing ".") // 1. Insert "." at the end of this span // 2. Replace the "Date." at the end of the span with "DateTime." // We need partial parsing to accept case #2. string oldText = GetOldText(target, change); PartialParseResult result = PartialParseResult.Rejected; if (EndsWithDot(oldText) && EndsWithDot(change.NewText)) { result = PartialParseResult.Accepted; if (!AcceptTrailingDot) { result |= PartialParseResult.Provisional; } } return result; } private PartialParseResult HandleDeletion(Span target, char previousChar, TextChange change) { // What's left after deleting? if (previousChar == '.') { return TryAcceptChange(target, change, PartialParseResult.Accepted | PartialParseResult.Provisional); } else if (ParserHelpers.IsIdentifierPart(previousChar)) { return TryAcceptChange(target, change); } else { return PartialParseResult.Rejected; } } private PartialParseResult HandleInsertion(Span target, char previousChar, TextChange change) { // What are we inserting after? if (previousChar == '.') { return HandleInsertionAfterDot(target, change); } else if (ParserHelpers.IsIdentifierPart(previousChar) || previousChar == ')' || previousChar == ']') { return HandleInsertionAfterIdPart(target, change); } else { return PartialParseResult.Rejected; } } private PartialParseResult HandleInsertionAfterIdPart(Span target, TextChange change) { // If the insertion is a full identifier part, accept it if (ParserHelpers.IsIdentifier(change.NewText, requireIdentifierStart: false)) { return TryAcceptChange(target, change); } else if (EndsWithDot(change.NewText)) { // Accept it, possibly provisionally PartialParseResult result = PartialParseResult.Accepted; if (!AcceptTrailingDot) { result |= PartialParseResult.Provisional; } return TryAcceptChange(target, change, result); } else { return PartialParseResult.Rejected; } } private static bool EndsWithDot(string content) { return (content.Length == 1 && content[0] == '.') || (content[content.Length - 1] == '.' && content.Take(content.Length - 1).All(ParserHelpers.IsIdentifierPart)); } private PartialParseResult HandleInsertionAfterDot(Span target, TextChange change) { // If the insertion is a full identifier or another dot, accept it if (ParserHelpers.IsIdentifier(change.NewText) || change.NewText == ".") { return TryAcceptChange(target, change); } return PartialParseResult.Rejected; } private PartialParseResult TryAcceptChange(Span target, TextChange change, PartialParseResult acceptResult = PartialParseResult.Accepted) { string content = change.ApplyChange(target); if (StartsWithKeyword(content)) { return PartialParseResult.Rejected | PartialParseResult.SpanContextChanged; } return acceptResult; } private bool StartsWithKeyword(string newContent) { using (StringReader reader = new StringReader(newContent)) { return Keywords.Contains(reader.ReadWhile(ParserHelpers.IsIdentifierPart)); } } } } ================================================ FILE: src/System.Web.Razor/Editor/RazorEditorTrace.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Razor.Resources; using System.Web.Razor.Text; namespace System.Web.Razor.Editor { internal static class RazorEditorTrace { private static bool? _enabled; private static bool IsEnabled() { if (_enabled == null) { bool enabled; if (Boolean.TryParse(Environment.GetEnvironmentVariable("RAZOR_EDITOR_TRACE"), out enabled)) { Trace.WriteLine(String.Format( CultureInfo.CurrentCulture, RazorResources.Trace_Startup, enabled ? RazorResources.Trace_Enabled : RazorResources.Trace_Disabled)); _enabled = enabled; } else { _enabled = false; } } return _enabled.Value; } [Conditional("EDITOR_TRACING")] public static void TraceLine(string format, params object[] args) { if (IsEnabled()) { Trace.WriteLine(String.Format( CultureInfo.CurrentCulture, RazorResources.Trace_Format, String.Format(CultureInfo.CurrentCulture, format, args))); } } } } ================================================ FILE: src/System.Web.Razor/Editor/SingleLineMarkupEditHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Editor { public class SingleLineMarkupEditHandler : SpanEditHandler { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public SingleLineMarkupEditHandler(Func> tokenizer) : base(tokenizer) { } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public SingleLineMarkupEditHandler(Func> tokenizer, AcceptedCharacters accepted) : base(tokenizer, accepted) { } } } ================================================ FILE: src/System.Web.Razor/Editor/SpanEditHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Editor { // Manages edits to a span public class SpanEditHandler { [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public SpanEditHandler(Func> tokenizer) : this(tokenizer, AcceptedCharacters.Any) { } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public SpanEditHandler(Func> tokenizer, AcceptedCharacters accepted) { AcceptedCharacters = accepted; Tokenizer = tokenizer; } public AcceptedCharacters AcceptedCharacters { get; set; } /// /// Provides a set of hints to editors which may be manipulating the document in which this span is located. /// public EditorHints EditorHints { get; set; } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public Func> Tokenizer { get; set; } public static SpanEditHandler CreateDefault() { return CreateDefault(s => Enumerable.Empty()); } [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func is the recommended delegate type and requires this level of nesting.")] public static SpanEditHandler CreateDefault(Func> tokenizer) { return new SpanEditHandler(tokenizer); } public virtual EditResult ApplyChange(Span target, TextChange change) { return ApplyChange(target, change, force: false); } public virtual EditResult ApplyChange(Span target, TextChange change, bool force) { PartialParseResult result = PartialParseResult.Accepted; TextChange normalized = change.Normalize(); if (!force) { result = CanAcceptChange(target, normalized); } // If the change is accepted then apply the change if (result.HasFlag(PartialParseResult.Accepted)) { return new EditResult(result, UpdateSpan(target, normalized)); } return new EditResult(result, new SpanBuilder(target)); } public virtual bool OwnsChange(Span target, TextChange change) { int end = target.Start.AbsoluteIndex + target.Length; int changeOldEnd = change.OldPosition + change.OldLength; return change.OldPosition >= target.Start.AbsoluteIndex && (changeOldEnd < end || (changeOldEnd == end && AcceptedCharacters != AcceptedCharacters.None)); } protected virtual PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange) { return PartialParseResult.Rejected; } protected virtual SpanBuilder UpdateSpan(Span target, TextChange normalizedChange) { string newContent = normalizedChange.ApplyChange(target); SpanBuilder newSpan = new SpanBuilder(target); newSpan.ClearSymbols(); foreach (ISymbol sym in Tokenizer(newContent)) { sym.OffsetStart(target.Start); newSpan.Accept(sym); } if (target.Next != null) { SourceLocation newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent); target.Next.ChangeStart(newEnd); } return newSpan; } protected internal static bool IsAtEndOfFirstLine(Span target, TextChange change) { int endOfFirstLine = target.Content.IndexOfAny(new char[] { (char)0x000d, (char)0x000a, (char)0x2028, (char)0x2029 }); return (endOfFirstLine == -1 || (change.OldPosition - target.Start.AbsoluteIndex) <= endOfFirstLine); } /// /// Returns true if the specified change is an insertion of text at the end of this span. /// protected internal static bool IsEndInsertion(Span target, TextChange change) { return change.IsInsert && IsAtEndOfSpan(target, change); } /// /// Returns true if the specified change is an insertion of text at the end of this span. /// protected internal static bool IsEndDeletion(Span target, TextChange change) { return change.IsDelete && IsAtEndOfSpan(target, change); } /// /// Returns true if the specified change is a replacement of text at the end of this span. /// protected internal static bool IsEndReplace(Span target, TextChange change) { return change.IsReplace && IsAtEndOfSpan(target, change); } [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method should only be used on Spans")] protected internal static bool IsAtEndOfSpan(Span target, TextChange change) { return (change.OldPosition + change.OldLength) == (target.Start.AbsoluteIndex + target.Length); } /// /// Returns the old text referenced by the change. /// /// /// If the content has already been updated by applying the change, this data will be _invalid_ /// protected internal static string GetOldText(Span target, TextChange change) { return target.Content.Substring(change.OldPosition - target.Start.AbsoluteIndex, change.OldLength); } // Is the specified span to the right of this span and immediately adjacent? internal static bool IsAdjacentOnRight(Span target, Span other) { return target.Start.AbsoluteIndex < other.Start.AbsoluteIndex && target.Start.AbsoluteIndex + target.Length == other.Start.AbsoluteIndex; } // Is the specified span to the left of this span and immediately adjacent? internal static bool IsAdjacentOnLeft(Span target, Span other) { return other.Start.AbsoluteIndex < target.Start.AbsoluteIndex && other.Start.AbsoluteIndex + other.Length == target.Start.AbsoluteIndex; } public override string ToString() { return GetType().Name + ";Accepts:" + AcceptedCharacters + ((EditorHints == EditorHints.None) ? String.Empty : (";Hints: " + EditorHints.ToString())); } public override bool Equals(object obj) { SpanEditHandler other = obj as SpanEditHandler; return other != null && AcceptedCharacters == other.AcceptedCharacters && EditorHints == other.EditorHints; } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(AcceptedCharacters) .Add(EditorHints) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Generator/AddImportCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Linq; using System.Web.Razor.Parser.SyntaxTree; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class AddImportCodeGenerator : SpanCodeGenerator { public AddImportCodeGenerator(string ns, int namespaceKeywordLength) { Namespace = ns; NamespaceKeywordLength = namespaceKeywordLength; } public string Namespace { get; private set; } public int NamespaceKeywordLength { get; set; } public override void GenerateCode(Span target, CodeGeneratorContext context) { // Try to find the namespace in the existing imports string ns = Namespace; if (!String.IsNullOrEmpty(ns) && Char.IsWhiteSpace(ns[0])) { ns = ns.Substring(1); } CodeNamespaceImport import = context.Namespace .Imports .OfType() .Where(i => String.Equals(i.Namespace, ns.Trim(), StringComparison.Ordinal)) .FirstOrDefault(); if (import == null) { // It doesn't exist, create it import = new CodeNamespaceImport(ns); context.Namespace.Imports.Add(import); } // Attach our info to the existing/new import. import.LinePragma = context.GenerateLinePragma(target); } public override string ToString() { return "Import:" + Namespace + ";KwdLen:" + NamespaceKeywordLength; } public override bool Equals(object obj) { AddImportCodeGenerator other = obj as AddImportCodeGenerator; return other != null && String.Equals(Namespace, other.Namespace, StringComparison.Ordinal) && NamespaceKeywordLength == other.NamespaceKeywordLength; } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(Namespace) .Add(NamespaceKeywordLength) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Generator/AttributeBlockCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class AttributeBlockCodeGenerator : BlockCodeGenerator { public AttributeBlockCodeGenerator(string name, LocationTagged prefix, LocationTagged suffix) { Name = name; Prefix = prefix; Suffix = suffix; } public string Name { get; private set; } public LocationTagged Prefix { get; private set; } public LocationTagged Suffix { get; private set; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; // Don't generate anything! } context.FlushBufferedStatement(); context.AddStatement(context.BuildCodeString(cw => { if (!String.IsNullOrEmpty(context.TargetWriterName)) { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteAttributeToMethodName); cw.WriteSnippet(context.TargetWriterName); cw.WriteParameterSeparator(); } else { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteAttributeMethodName); } cw.WriteStringLiteral(Name); cw.WriteParameterSeparator(); cw.WriteLocationTaggedString(Prefix); cw.WriteParameterSeparator(); cw.WriteLocationTaggedString(Suffix); // In VB, we need a line continuation cw.WriteLineContinuation(); })); } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; // Don't generate anything! } context.FlushBufferedStatement(); context.AddStatement(context.BuildCodeString(cw => { cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); })); } public override string ToString() { return String.Format(CultureInfo.CurrentCulture, "Attr:{0},{1:F},{2:F}", Name, Prefix, Suffix); } public override bool Equals(object obj) { AttributeBlockCodeGenerator other = obj as AttributeBlockCodeGenerator; return other != null && String.Equals(other.Name, Name, StringComparison.Ordinal) && Equals(other.Prefix, Prefix) && Equals(other.Suffix, Suffix); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(Name) .Add(Prefix) .Add(Suffix) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Generator/BaseCodeWriter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Generator { internal abstract class BaseCodeWriter : CodeWriter { public override void WriteSnippet(string snippet) { InnerWriter.Write(snippet); } protected internal override void EmitStartMethodInvoke(string methodName) { EmitStartMethodInvoke(methodName, new string[0]); } protected internal override void EmitStartMethodInvoke(string methodName, params string[] genericArguments) { InnerWriter.Write(methodName); if (genericArguments != null && genericArguments.Length > 0) { WriteStartGenerics(); for (int i = 0; i < genericArguments.Length; i++) { if (i > 0) { WriteParameterSeparator(); } WriteSnippet(genericArguments[i]); } WriteEndGenerics(); } InnerWriter.Write("("); } protected internal override void EmitEndMethodInvoke() { InnerWriter.Write(")"); } protected internal override void EmitEndConstructor() { InnerWriter.Write(")"); } protected internal override void EmitEndLambdaExpression() { } public override void WriteParameterSeparator() { InnerWriter.Write(", "); } protected internal void WriteCommaSeparatedList(T[] items, Action writeItemAction) { for (int i = 0; i < items.Length; i++) { if (i > 0) { InnerWriter.Write(", "); } writeItemAction(items[i]); } } protected internal virtual void WriteStartGenerics() { } protected internal virtual void WriteEndGenerics() { } } } ================================================ FILE: src/System.Web.Razor/Generator/BlockCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public abstract class BlockCodeGenerator : IBlockCodeGenerator { [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] public static readonly IBlockCodeGenerator Null = new NullBlockCodeGenerator(); public virtual void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { } public virtual void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { } public override bool Equals(object obj) { return (obj as IBlockCodeGenerator) != null; } public override int GetHashCode() { return base.GetHashCode(); } private class NullBlockCodeGenerator : IBlockCodeGenerator { public void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { } public void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { } public override string ToString() { return "None"; } } } } ================================================ FILE: src/System.Web.Razor/Generator/CSharpCodeWriter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.Web.Razor.Generator { internal class CSharpCodeWriter : BaseCodeWriter { protected internal override void WriteStartGenerics() { InnerWriter.Write("<"); } protected internal override void WriteEndGenerics() { InnerWriter.Write(">"); } public override int WriteVariableDeclaration(string type, string name, string value) { InnerWriter.Write(type); InnerWriter.Write(" "); InnerWriter.Write(name); if (!String.IsNullOrEmpty(value)) { InnerWriter.Write(" = "); InnerWriter.Write(value); } else { InnerWriter.Write(" = null"); } return 0; } public override void WriteDisableUnusedFieldWarningPragma() { InnerWriter.Write("#pragma warning disable 219"); } public override void WriteRestoreUnusedFieldWarningPragma() { InnerWriter.Write("#pragma warning restore 219"); } public override void WriteStringLiteral(string literal) { if (literal == null) { throw new ArgumentNullException("literal"); } // From CSharpCodeProvider in CodeDOM // If the string is short, use C style quoting (e.g "\r\n") // Also do it if it is too long to fit in one line // If the string contains '\0', verbatim style won't work. if (literal.Length >= 256 && literal.Length <= 1500 && literal.IndexOf('\0') == -1) { WriteVerbatimStringLiteral(literal); } else { WriteCStyleStringLiteral(literal); } } private void WriteVerbatimStringLiteral(string literal) { // From CSharpCodeGenerator.QuoteSnippetStringVerbatim in CodeDOM InnerWriter.Write("@\""); for (int i = 0; i < literal.Length; i++) { if (literal[i] == '\"') { InnerWriter.Write("\"\""); } else { InnerWriter.Write(literal[i]); } } InnerWriter.Write("\""); } private void WriteCStyleStringLiteral(string literal) { // From CSharpCodeGenerator.QuoteSnippetStringCStyle in CodeDOM InnerWriter.Write("\""); for (int i = 0; i < literal.Length; i++) { switch (literal[i]) { case '\r': InnerWriter.Write("\\r"); break; case '\t': InnerWriter.Write("\\t"); break; case '\"': InnerWriter.Write("\\\""); break; case '\'': InnerWriter.Write("\\\'"); break; case '\\': InnerWriter.Write("\\\\"); break; case '\0': InnerWriter.Write("\\\0"); break; case '\n': InnerWriter.Write("\\n"); break; case '\u2028': case '\u2029': // Inlined CSharpCodeGenerator.AppendEscapedChar InnerWriter.Write("\\u"); InnerWriter.Write(((int)literal[i]).ToString("X4", CultureInfo.InvariantCulture)); break; default: InnerWriter.Write(literal[i]); break; } if (i > 0 && i % 80 == 0) { // If current character is a high surrogate and the following // character is a low surrogate, don't break them. // Otherwise when we write the string to a file, we might lose // the characters. if (Char.IsHighSurrogate(literal[i]) && (i < literal.Length - 1) && Char.IsLowSurrogate(literal[i + 1])) { InnerWriter.Write(literal[++i]); } InnerWriter.Write("\" +"); InnerWriter.Write(Environment.NewLine); InnerWriter.Write('\"'); } } InnerWriter.Write("\""); } public override void WriteEndStatement() { InnerWriter.WriteLine(";"); } public override void WriteIdentifier(string identifier) { InnerWriter.Write("@" + identifier); } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Lowercase is intended here. C# boolean literals are all lowercase")] public override void WriteBooleanLiteral(bool value) { WriteSnippet(value.ToString().ToLowerInvariant()); } protected internal override void EmitStartLambdaExpression(string[] parameterNames) { if (parameterNames == null) { throw new ArgumentNullException("parameterNames"); } if (parameterNames.Length == 0 || parameterNames.Length > 1) { InnerWriter.Write("("); } WriteCommaSeparatedList(parameterNames, InnerWriter.Write); if (parameterNames.Length == 0 || parameterNames.Length > 1) { InnerWriter.Write(")"); } InnerWriter.Write(" => "); } protected internal override void EmitStartLambdaDelegate(string[] parameterNames) { if (parameterNames == null) { throw new ArgumentNullException("parameterNames"); } EmitStartLambdaExpression(parameterNames); InnerWriter.WriteLine("{"); } protected internal override void EmitEndLambdaDelegate() { InnerWriter.Write("}"); } protected internal override void EmitStartConstructor(string typeName) { if (typeName == null) { throw new ArgumentNullException("typeName"); } InnerWriter.Write("new "); InnerWriter.Write(typeName); InnerWriter.Write("("); } public override void WriteReturn() { InnerWriter.Write("return "); } public override void WriteLinePragma(int? lineNumber, string fileName) { InnerWriter.WriteLine(); if (lineNumber != null) { InnerWriter.Write("#line "); InnerWriter.Write(lineNumber); InnerWriter.Write(" \""); InnerWriter.Write(fileName); InnerWriter.Write("\""); InnerWriter.WriteLine(); } else { InnerWriter.WriteLine("#line default"); InnerWriter.WriteLine("#line hidden"); } } public override void WriteHiddenLinePragma() { InnerWriter.WriteLine("#line hidden"); } public override void WriteHelperHeaderPrefix(string templateTypeName, bool isStatic) { InnerWriter.Write("public "); if (isStatic) { InnerWriter.Write("static "); } InnerWriter.Write(templateTypeName); InnerWriter.Write(" "); } } } ================================================ FILE: src/System.Web.Razor/Generator/CSharpRazorCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Diagnostics.CodeAnalysis; namespace System.Web.Razor.Generator { public class CSharpRazorCodeGenerator : RazorCodeGenerator { private const string HiddenLinePragma = "#line hidden"; public CSharpRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host) : base(className, rootNamespaceName, sourceFileName, host) { } internal override Func CodeWriterFactory { get { return () => new CSharpCodeWriter(); } } [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.CodeDom.CodeSnippetTypeMember.#ctor(System.String)", Justification = "Value is never to be localized")] protected override void Initialize(CodeGeneratorContext context) { base.Initialize(context); context.GeneratedClass.Members.Insert(0, new CodeSnippetTypeMember(HiddenLinePragma)); } } } ================================================ FILE: src/System.Web.Razor/Generator/CodeGenerationCompleteEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class CodeGenerationCompleteEventArgs : EventArgs { public CodeGenerationCompleteEventArgs(string virtualPath, string physicalPath, CodeCompileUnit generatedCode) { if (String.IsNullOrEmpty(virtualPath)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "virtualPath"); } if (generatedCode == null) { throw new ArgumentNullException("generatedCode"); } VirtualPath = virtualPath; PhysicalPath = physicalPath; GeneratedCode = generatedCode; } public CodeCompileUnit GeneratedCode { get; private set; } public string VirtualPath { get; private set; } public string PhysicalPath { get; private set; } } } ================================================ FILE: src/System.Web.Razor/Generator/CodeGeneratorContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Text; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Utils; namespace System.Web.Razor.Generator { public class CodeGeneratorContext { private const string DesignTimeHelperMethodName = "__RazorDesignTimeHelpers__"; private int _nextDesignTimePragmaId = 1; private bool _expressionHelperVariableWriten; private CodeMemberMethod _designTimeHelperMethod; private StatementBuffer _currentBuffer = new StatementBuffer(); private CodeGeneratorContext() { ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput; } // Internal/Private state. Technically consumers might want to use some of these but they can implement them independently if necessary. // It's way safer to make them internal for now, especially with the code generator stuff in a bit of flux. internal ExpressionRenderingMode ExpressionRenderingMode { get; set; } private Action StatementCollector { get; set; } private Func CodeWriterFactory { get; set; } public string SourceFile { get; internal set; } public CodeCompileUnit CompileUnit { get; internal set; } public CodeNamespace Namespace { get; internal set; } public CodeTypeDeclaration GeneratedClass { get; internal set; } public RazorEngineHost Host { get; private set; } public IDictionary CodeMappings { get; private set; } public string TargetWriterName { get; set; } public CodeMemberMethod TargetMethod { get; set; } public string CurrentBufferedStatement { get { return _currentBuffer == null ? String.Empty : _currentBuffer.Builder.ToString(); } } public static CodeGeneratorContext Create(RazorEngineHost host, string className, string rootNamespace, string sourceFile, bool shouldGenerateLinePragmas) { return Create(host, null, className, rootNamespace, sourceFile, shouldGenerateLinePragmas); } internal static CodeGeneratorContext Create(RazorEngineHost host, Func writerFactory, string className, string rootNamespace, string sourceFile, bool shouldGenerateLinePragmas) { CodeGeneratorContext context = new CodeGeneratorContext() { Host = host, CodeWriterFactory = writerFactory, SourceFile = shouldGenerateLinePragmas ? sourceFile : null, CompileUnit = new CodeCompileUnit(), Namespace = new CodeNamespace(rootNamespace), GeneratedClass = new CodeTypeDeclaration(className) { IsClass = true }, TargetMethod = new CodeMemberMethod() { Name = host.GeneratedClassContext.ExecuteMethodName, Attributes = MemberAttributes.Override | MemberAttributes.Public }, CodeMappings = new Dictionary() }; context.CompileUnit.Namespaces.Add(context.Namespace); context.Namespace.Types.Add(context.GeneratedClass); context.GeneratedClass.Members.Add(context.TargetMethod); context.Namespace.Imports.AddRange(host.NamespaceImports .Select(s => new CodeNamespaceImport(s)) .ToArray()); return context; } public void AddDesignTimeHelperStatement(CodeSnippetStatement statement) { if (_designTimeHelperMethod == null) { _designTimeHelperMethod = new CodeMemberMethod() { Name = DesignTimeHelperMethodName, Attributes = MemberAttributes.Private }; _designTimeHelperMethod.Statements.Add( new CodeSnippetStatement(BuildCodeString(cw => cw.WriteDisableUnusedFieldWarningPragma()))); _designTimeHelperMethod.Statements.Add( new CodeSnippetStatement(BuildCodeString(cw => cw.WriteRestoreUnusedFieldWarningPragma()))); GeneratedClass.Members.Insert(0, _designTimeHelperMethod); } _designTimeHelperMethod.Statements.Insert(_designTimeHelperMethod.Statements.Count - 1, statement); } [SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "generatedCodeStart+1", Justification = "There is no risk of overflow in this case")] public int AddCodeMapping(SourceLocation sourceLocation, int generatedCodeStart, int generatedCodeLength) { if (generatedCodeStart == Int32.MaxValue) { throw new ArgumentOutOfRangeException("generatedCodeStart"); } GeneratedCodeMapping mapping = new GeneratedCodeMapping( startOffset: sourceLocation.AbsoluteIndex, startLine: sourceLocation.LineIndex + 1, startColumn: sourceLocation.CharacterIndex + 1, startGeneratedColumn: generatedCodeStart + 1, codeLength: generatedCodeLength); int id = _nextDesignTimePragmaId++; CodeMappings[id] = mapping; return id; } [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")] public CodeLinePragma GenerateLinePragma(Span target) { return GenerateLinePragma(target, 0); } [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")] public CodeLinePragma GenerateLinePragma(Span target, int generatedCodeStart) { return GenerateLinePragma(target, generatedCodeStart, target.Content.Length); } [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method requires that a Span be provided")] public CodeLinePragma GenerateLinePragma(Span target, int generatedCodeStart, int codeLength) { return GenerateLinePragma(target.Start, generatedCodeStart, codeLength); } public CodeLinePragma GenerateLinePragma(SourceLocation start, int generatedCodeStart, int codeLength) { if (!String.IsNullOrEmpty(SourceFile)) { if (Host.DesignTimeMode) { int mappingId = AddCodeMapping(start, generatedCodeStart, codeLength); return new CodeLinePragma(SourceFile, mappingId); } return new CodeLinePragma(SourceFile, start.LineIndex + 1); } return null; } public void BufferStatementFragment(Span sourceSpan) { BufferStatementFragment(sourceSpan.Content, sourceSpan); } public void BufferStatementFragment(string fragment) { BufferStatementFragment(fragment, null); } public void BufferStatementFragment(string fragment, Span sourceSpan) { if (sourceSpan != null && _currentBuffer.LinePragmaSpan == null) { _currentBuffer.LinePragmaSpan = sourceSpan; // Pad the output as necessary int start = _currentBuffer.Builder.Length; if (_currentBuffer.GeneratedCodeStart != null) { start = _currentBuffer.GeneratedCodeStart.Value; } int paddingLength; // unused, in this case there is enough context in the original code to calculate the right padding length // (padded.Length - _currentBuffer.Builder.Length) string padded = CodeGeneratorPaddingHelper.Pad(Host, _currentBuffer.Builder.ToString(), sourceSpan, start, out paddingLength); _currentBuffer.GeneratedCodeStart = start + (padded.Length - _currentBuffer.Builder.Length); _currentBuffer.Builder.Clear(); _currentBuffer.Builder.Append(padded); } _currentBuffer.Builder.Append(fragment); } public void MarkStartOfGeneratedCode() { _currentBuffer.MarkStart(); } public void MarkEndOfGeneratedCode() { _currentBuffer.MarkEnd(); } public void FlushBufferedStatement() { if (_currentBuffer.Builder.Length > 0) { CodeLinePragma pragma = null; if (_currentBuffer.LinePragmaSpan != null) { int start = _currentBuffer.Builder.Length; if (_currentBuffer.GeneratedCodeStart != null) { start = _currentBuffer.GeneratedCodeStart.Value; } int len = _currentBuffer.Builder.Length - start; if (_currentBuffer.CodeLength != null) { len = _currentBuffer.CodeLength.Value; } pragma = GenerateLinePragma(_currentBuffer.LinePragmaSpan, start, len); } AddStatement(_currentBuffer.Builder.ToString(), pragma); _currentBuffer.Reset(); } } public void AddStatement(string generatedCode) { AddStatement(generatedCode, null); } public void AddStatement(string body, CodeLinePragma pragma) { if (StatementCollector == null) { TargetMethod.Statements.Add(new CodeSnippetStatement(body) { LinePragma = pragma }); } else { StatementCollector(body, pragma); } } public void EnsureExpressionHelperVariable() { if (!_expressionHelperVariableWriten) { GeneratedClass.Members.Insert(0, new CodeMemberField(typeof(object), "__o") { Attributes = MemberAttributes.Private | MemberAttributes.Static }); _expressionHelperVariableWriten = true; } } public IDisposable ChangeStatementCollector(Action collector) { Action oldCollector = StatementCollector; StatementCollector = collector; return new DisposableAction(() => { StatementCollector = oldCollector; }); } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We explicitly want the lower-case string here")] public void AddContextCall(Span contentSpan, string methodName, bool isLiteral) { AddStatement(BuildCodeString(cw => { cw.WriteStartMethodInvoke(methodName); if (!String.IsNullOrEmpty(TargetWriterName)) { cw.WriteSnippet(TargetWriterName); cw.WriteParameterSeparator(); } cw.WriteStringLiteral(Host.InstrumentedSourceFilePath); cw.WriteParameterSeparator(); cw.WriteSnippet(contentSpan.Start.AbsoluteIndex.ToString(CultureInfo.InvariantCulture)); cw.WriteParameterSeparator(); cw.WriteSnippet(contentSpan.Content.Length.ToString(CultureInfo.InvariantCulture)); cw.WriteParameterSeparator(); cw.WriteSnippet(isLiteral.ToString().ToLowerInvariant()); cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); })); } internal CodeWriter CreateCodeWriter() { Debug.Assert(CodeWriterFactory != null); if (CodeWriterFactory == null) { throw new InvalidOperationException(RazorResources.CreateCodeWriter_NoCodeWriter); } return CodeWriterFactory(); } internal string BuildCodeString(Action action) { using (CodeWriter cw = CodeWriterFactory()) { action(cw); return cw.Content; } } private class StatementBuffer { public StringBuilder Builder = new StringBuilder(); public int? GeneratedCodeStart; public int? CodeLength; public Span LinePragmaSpan; public void Reset() { Builder.Clear(); GeneratedCodeStart = null; CodeLength = null; LinePragmaSpan = null; } public void MarkStart() { GeneratedCodeStart = Builder.Length; } public void MarkEnd() { CodeLength = Builder.Length - GeneratedCodeStart; } } } } ================================================ FILE: src/System.Web.Razor/Generator/CodeGeneratorPaddingHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { internal static class CodeGeneratorPaddingHelper { private static readonly char[] _newLineChars = { '\r', '\n' }; // there is some duplicity of code here, but its very simple and since this is a host path, I'd rather not create another class to encapsulate the data. public static int PaddingCharCount(RazorEngineHost host, Span target, int generatedStart) { int padding = CalculatePadding(host, target, generatedStart); if (host.DesignTimeMode && host.IsIndentingWithTabs) { int spaces; int tabs = Math.DivRem(padding, host.TabSize, out spaces); return tabs + spaces; } else { return padding; } } // Special case for statement padding to account for brace positioning in the editor. public static string PadStatement(RazorEngineHost host, string code, Span target, ref int startGeneratedCode, out int paddingCharCount) { if (host == null) { throw new ArgumentNullException("host"); } if (target == null) { throw new ArgumentNullException("target"); } // We are passing 0 rather than startgeneratedcode intentionally (keeping v2 behavior). int padding = CalculatePadding(host, target, 0); // We treat statement padding specially so for brace positioning, so that in the following example: // @if (foo > 0) // { // } // // the braces shows up under the @ rather than under the if. if (host.DesignTimeMode && padding > 0 && target.Previous.Kind == SpanKind.Transition && // target.Previous is guaranteed to be none null if you got any padding. String.Equals(target.Previous.Content, SyntaxConstants.TransitionString)) { padding--; startGeneratedCode--; } string generatedCode = PadInternal(host, code, padding, out paddingCharCount); return generatedCode; } public static string Pad(RazorEngineHost host, string code, Span target, out int paddingCharCount) { int padding = CalculatePadding(host, target, 0); return PadInternal(host, code, padding, out paddingCharCount); } public static string Pad(RazorEngineHost host, string code, Span target, int generatedStart, out int paddingCharCount) { int padding = CalculatePadding(host, target, generatedStart); return PadInternal(host, code, padding, out paddingCharCount); } // internal for unit testing only, not intended to be used directly in code internal static int CalculatePadding(RazorEngineHost host, Span target, int generatedStart) { if (host == null) { throw new ArgumentNullException("host"); } if (target == null) { throw new ArgumentNullException("target"); } int padding; padding = CollectSpacesAndTabs(target, host.TabSize) - generatedStart; // if we add generated text that is longer than the padding we wanted to insert we have no recourse and we have to skip padding // example: // Razor code at column zero: @somecode() // Generated code will be: // In design time: __o = somecode(); // In Run time: Write(somecode()); // // In both cases the padding would have been 1 space to remote the space the @ symbol takes, which will be smaller than the 6 chars the hidden generated code takes. if (padding < 0) { padding = 0; } return padding; } private static string PadInternal(RazorEngineHost host, string code, int padding, out int paddingCharCount) { if (host.DesignTimeMode && host.IsIndentingWithTabs) { int spaces; int tabs = Math.DivRem(padding, host.TabSize, out spaces); paddingCharCount = tabs + spaces; return new string('\t', tabs) + new string(' ', spaces) + code; } else { paddingCharCount = padding; return code.PadLeft(padding + code.Length, ' '); } } private static int CollectSpacesAndTabs(Span target, int tabSize) { Span firstSpanInLine = target; string currentContent = null; while (firstSpanInLine.Previous != null) { // When scanning previous spans we need to be break down the spans with spaces. // Because the parser doesn't so for example a span looking like \n\n\t needs to be broken down, and we should just grab the \t. String previousContent = firstSpanInLine.Previous.Content ?? String.Empty; int lastNewLineIndex = previousContent.LastIndexOfAny(_newLineChars); if (lastNewLineIndex < 0) { firstSpanInLine = firstSpanInLine.Previous; } else { if (lastNewLineIndex != previousContent.Length - 1) { firstSpanInLine = firstSpanInLine.Previous; currentContent = previousContent.Substring(lastNewLineIndex + 1); } break; } } // We need to walk from the beginning of the line, because space + tab(tabSize) = tabSize columns, but tab(tabSize) + space = tabSize+1 columns. Span currentSpanInLine = firstSpanInLine; if (currentContent == null) { currentContent = currentSpanInLine.Content; } int padding = 0; while (currentSpanInLine != target) { if (currentContent != null) { for (int i = 0; i < currentContent.Length; i++) { if (currentContent[i] == '\t') { // Example: // : // iter 1) 1 // iter 2) 2 // iter 3) 4 = 2 + (4 - 2) // iter 4) 8 = 4 + (4 - 0) padding = padding + (tabSize - (padding % tabSize)); } else { padding++; } } } currentSpanInLine = currentSpanInLine.Next; currentContent = currentSpanInLine.Content; } return padding; } } } ================================================ FILE: src/System.Web.Razor/Generator/CodeWriter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Globalization; using System.IO; namespace System.Web.Razor.Generator { // Utility class which helps write code snippets internal abstract class CodeWriter : IDisposable { private StringWriter _writer; protected CodeWriter() { } private enum WriterMode { Constructor, MethodCall, LambdaDelegate, LambdaExpression } public string Content { get { return InnerWriter.ToString(); } } public StringWriter InnerWriter { get { if (_writer == null) { _writer = new StringWriter(CultureInfo.InvariantCulture); } return _writer; } } public virtual bool SupportsMidStatementLinePragmas { get { return true; } } public abstract void WriteParameterSeparator(); public abstract void WriteReturn(); public abstract void WriteLinePragma(int? lineNumber, string fileName); public abstract void WriteHelperHeaderPrefix(string templateTypeName, bool isStatic); public abstract void WriteSnippet(string snippet); public abstract void WriteStringLiteral(string literal); public abstract int WriteVariableDeclaration(string type, string name, string value); public virtual void WriteLinePragma() { WriteLinePragma(null); } public virtual void WriteLinePragma(CodeLinePragma pragma) { if (pragma == null) { WriteLinePragma(null, null); } else { WriteLinePragma(pragma.LineNumber, pragma.FileName); } } public virtual void WriteHiddenLinePragma() { } public virtual void WriteDisableUnusedFieldWarningPragma() { } public virtual void WriteRestoreUnusedFieldWarningPragma() { } public virtual void WriteIdentifier(string identifier) { InnerWriter.Write(identifier); } public virtual void WriteHelperHeaderSuffix(string templateTypeName) { } public virtual void WriteHelperTrailer() { } public void WriteStartMethodInvoke(string methodName) { EmitStartMethodInvoke(methodName); } public void WriteStartMethodInvoke(string methodName, params string[] genericArguments) { EmitStartMethodInvoke(methodName, genericArguments); } public void WriteEndMethodInvoke() { EmitEndMethodInvoke(); } public virtual void WriteEndStatement() { } public virtual void WriteStartAssignment(string variableName) { InnerWriter.Write(variableName); InnerWriter.Write(" = "); } public void WriteStartLambdaExpression(params string[] parameterNames) { EmitStartLambdaExpression(parameterNames); } public void WriteStartConstructor(string typeName) { EmitStartConstructor(typeName); } public void WriteStartLambdaDelegate(params string[] parameterNames) { EmitStartLambdaDelegate(parameterNames); } public void WriteEndLambdaExpression() { EmitEndLambdaExpression(); } public void WriteEndConstructor() { EmitEndConstructor(); } public void WriteEndLambdaDelegate() { EmitEndLambdaDelegate(); } public virtual void WriteLineContinuation() { } public virtual void WriteBooleanLiteral(bool value) { WriteSnippet(value.ToString(CultureInfo.InvariantCulture)); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Clear() { if (InnerWriter != null) { InnerWriter.GetStringBuilder().Clear(); } } public CodeSnippetStatement ToStatement() { return new CodeSnippetStatement(Content); } public CodeSnippetTypeMember ToTypeMember() { return new CodeSnippetTypeMember(Content); } protected internal abstract void EmitStartLambdaDelegate(string[] parameterNames); protected internal abstract void EmitStartLambdaExpression(string[] parameterNames); protected internal abstract void EmitStartConstructor(string typeName); protected internal abstract void EmitStartMethodInvoke(string methodName); protected internal virtual void EmitStartMethodInvoke(string methodName, params string[] genericArguments) { EmitStartMethodInvoke(methodName); } protected internal abstract void EmitEndLambdaDelegate(); protected internal abstract void EmitEndLambdaExpression(); protected internal abstract void EmitEndConstructor(); protected internal abstract void EmitEndMethodInvoke(); protected virtual void Dispose(bool disposing) { if (disposing && _writer != null) { _writer.Dispose(); } } } } ================================================ FILE: src/System.Web.Razor/Generator/CodeWriterExtensions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web.Razor.Text; namespace System.Web.Razor.Generator { internal static class CodeWriterExtensions { public static void WriteLocationTaggedString(this CodeWriter writer, LocationTagged value) { writer.WriteStartMethodInvoke("Tuple.Create"); writer.WriteStringLiteral(value.Value); writer.WriteParameterSeparator(); writer.WriteSnippet(value.Location.AbsoluteIndex.ToString(CultureInfo.CurrentCulture)); writer.WriteEndMethodInvoke(); } } } ================================================ FILE: src/System.Web.Razor/Generator/DynamicAttributeBlockCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Linq; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class DynamicAttributeBlockCodeGenerator : BlockCodeGenerator { private const string ValueWriterName = "__razor_attribute_value_writer"; private string _oldTargetWriter; private bool _isExpression; private ExpressionRenderingMode _oldRenderingMode; public DynamicAttributeBlockCodeGenerator(LocationTagged prefix, int offset, int line, int col) : this(prefix, new SourceLocation(offset, line, col)) { } public DynamicAttributeBlockCodeGenerator(LocationTagged prefix, SourceLocation valueStart) { Prefix = prefix; ValueStart = valueStart; } public LocationTagged Prefix { get; private set; } public SourceLocation ValueStart { get; private set; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; // Don't generate anything! } // What kind of block is nested within string generatedCode; Block child = target.Children.Where(n => n.IsBlock).Cast().FirstOrDefault(); if (child != null && child.Type == BlockType.Expression) { _isExpression = true; generatedCode = context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create"); cw.WriteLocationTaggedString(Prefix); cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create", "System.Object", "System.Int32"); }); _oldRenderingMode = context.ExpressionRenderingMode; context.ExpressionRenderingMode = ExpressionRenderingMode.InjectCode; } else { generatedCode = context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create"); cw.WriteLocationTaggedString(Prefix); cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create", "System.Object", "System.Int32"); cw.WriteStartConstructor(context.Host.GeneratedClassContext.TemplateTypeName); cw.WriteStartLambdaDelegate(ValueWriterName); }); } context.MarkEndOfGeneratedCode(); context.BufferStatementFragment(generatedCode); _oldTargetWriter = context.TargetWriterName; context.TargetWriterName = ValueWriterName; } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; // Don't generate anything! } string generatedCode; if (_isExpression) { generatedCode = context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteSnippet(ValueStart.AbsoluteIndex.ToString(CultureInfo.CurrentCulture)); cw.WriteEndMethodInvoke(); cw.WriteParameterSeparator(); // literal: false - This attribute value is not a literal value, it is dynamically generated cw.WriteBooleanLiteral(false); cw.WriteEndMethodInvoke(); cw.WriteLineContinuation(); }); context.ExpressionRenderingMode = _oldRenderingMode; } else { generatedCode = context.BuildCodeString(cw => { cw.WriteEndLambdaDelegate(); cw.WriteEndConstructor(); cw.WriteParameterSeparator(); cw.WriteSnippet(ValueStart.AbsoluteIndex.ToString(CultureInfo.CurrentCulture)); cw.WriteEndMethodInvoke(); cw.WriteParameterSeparator(); // literal: false - This attribute value is not a literal value, it is dynamically generated cw.WriteBooleanLiteral(false); cw.WriteEndMethodInvoke(); cw.WriteLineContinuation(); }); } context.AddStatement(generatedCode); context.TargetWriterName = _oldTargetWriter; } public override string ToString() { return String.Format(CultureInfo.CurrentCulture, "DynAttr:{0:F}", Prefix); } public override bool Equals(object obj) { DynamicAttributeBlockCodeGenerator other = obj as DynamicAttributeBlockCodeGenerator; return other != null && Equals(other.Prefix, Prefix); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(Prefix) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Generator/ExpressionCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class ExpressionCodeGenerator : HybridCodeGenerator { public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { if (context.Host.EnableInstrumentation && context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { Span contentSpan = target.Children .OfType() .Where(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup) .FirstOrDefault(); if (contentSpan != null) { context.AddContextCall(contentSpan, context.Host.GeneratedClassContext.BeginContextMethodName, false); } } string writeInvocation = context.BuildCodeString(cw => { if (context.Host.DesignTimeMode) { context.EnsureExpressionHelperVariable(); cw.WriteStartAssignment("__o"); } else if (context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { if (!String.IsNullOrEmpty(context.TargetWriterName)) { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteToMethodName); cw.WriteSnippet(context.TargetWriterName); cw.WriteParameterSeparator(); } else { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteMethodName); } } }); context.BufferStatementFragment(writeInvocation); context.MarkStartOfGeneratedCode(); } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { string endBlock = context.BuildCodeString(cw => { if (context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { if (!context.Host.DesignTimeMode) { cw.WriteEndMethodInvoke(); } cw.WriteEndStatement(); } else { cw.WriteLineContinuation(); } }); context.MarkEndOfGeneratedCode(); context.BufferStatementFragment(endBlock); context.FlushBufferedStatement(); if (context.Host.EnableInstrumentation && context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { Span contentSpan = target.Children .OfType() .Where(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup) .FirstOrDefault(); if (contentSpan != null) { context.AddContextCall(contentSpan, context.Host.GeneratedClassContext.EndContextMethodName, false); } } } public override void GenerateCode(Span target, CodeGeneratorContext context) { Span sourceSpan = null; if (context.CreateCodeWriter().SupportsMidStatementLinePragmas || context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { sourceSpan = target; } context.BufferStatementFragment(target.Content, sourceSpan); } public override string ToString() { return "Expr"; } public override bool Equals(object obj) { return obj is ExpressionCodeGenerator; } public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/ExpressionRenderingMode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Generator { public enum ExpressionRenderingMode { /// /// Indicates that expressions should be written to the output stream /// /// /// If @foo is rendered with WriteToOutput, the code generator would output the following code: /// /// Write(foo); /// WriteToOutput, /// /// Indicates that expressions should simply be placed as-is in the code, and the context in which /// the code exists will be used to render it /// /// /// If @foo is rendered with InjectCode, the code generator would output the following code: /// /// foo /// InjectCode } } ================================================ FILE: src/System.Web.Razor/Generator/GeneratedClassContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public struct GeneratedClassContext { public static readonly string DefaultWriteMethodName = "Write"; public static readonly string DefaultWriteLiteralMethodName = "WriteLiteral"; public static readonly string DefaultExecuteMethodName = "Execute"; public static readonly string DefaultLayoutPropertyName = "Layout"; public static readonly string DefaultWriteAttributeMethodName = "WriteAttribute"; public static readonly string DefaultWriteAttributeToMethodName = "WriteAttributeTo"; public static readonly GeneratedClassContext Default = new GeneratedClassContext(DefaultExecuteMethodName, DefaultWriteMethodName, DefaultWriteLiteralMethodName); public GeneratedClassContext(string executeMethodName, string writeMethodName, string writeLiteralMethodName) : this() { if (String.IsNullOrEmpty(executeMethodName)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "executeMethodName"), "executeMethodName"); } if (String.IsNullOrEmpty(writeMethodName)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "writeMethodName"), "writeMethodName"); } if (String.IsNullOrEmpty(writeLiteralMethodName)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "writeLiteralMethodName"), "writeLiteralMethodName"); } WriteMethodName = writeMethodName; WriteLiteralMethodName = writeLiteralMethodName; ExecuteMethodName = executeMethodName; WriteToMethodName = null; WriteLiteralToMethodName = null; TemplateTypeName = null; DefineSectionMethodName = null; LayoutPropertyName = DefaultLayoutPropertyName; WriteAttributeMethodName = DefaultWriteAttributeMethodName; WriteAttributeToMethodName = DefaultWriteAttributeToMethodName; } public GeneratedClassContext(string executeMethodName, string writeMethodName, string writeLiteralMethodName, string writeToMethodName, string writeLiteralToMethodName, string templateTypeName) : this(executeMethodName, writeMethodName, writeLiteralMethodName) { WriteToMethodName = writeToMethodName; WriteLiteralToMethodName = writeLiteralToMethodName; TemplateTypeName = templateTypeName; } public GeneratedClassContext(string executeMethodName, string writeMethodName, string writeLiteralMethodName, string writeToMethodName, string writeLiteralToMethodName, string templateTypeName, string defineSectionMethodName) : this(executeMethodName, writeMethodName, writeLiteralMethodName, writeToMethodName, writeLiteralToMethodName, templateTypeName) { DefineSectionMethodName = defineSectionMethodName; } public GeneratedClassContext(string executeMethodName, string writeMethodName, string writeLiteralMethodName, string writeToMethodName, string writeLiteralToMethodName, string templateTypeName, string defineSectionMethodName, string beginContextMethodName, string endContextMethodName) : this(executeMethodName, writeMethodName, writeLiteralMethodName, writeToMethodName, writeLiteralToMethodName, templateTypeName, defineSectionMethodName) { BeginContextMethodName = beginContextMethodName; EndContextMethodName = endContextMethodName; } public string WriteMethodName { get; private set; } public string WriteLiteralMethodName { get; private set; } public string WriteToMethodName { get; private set; } public string WriteLiteralToMethodName { get; private set; } public string ExecuteMethodName { get; private set; } // Optional Items public string BeginContextMethodName { get; set; } public string EndContextMethodName { get; set; } public string LayoutPropertyName { get; set; } public string DefineSectionMethodName { get; set; } public string TemplateTypeName { get; set; } public string WriteAttributeMethodName { get; set; } public string WriteAttributeToMethodName { get; set; } [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property is not a URL property")] public string ResolveUrlMethodName { get; set; } public bool AllowSections { get { return !String.IsNullOrEmpty(DefineSectionMethodName); } } public bool AllowTemplates { get { return !String.IsNullOrEmpty(TemplateTypeName); } } public bool SupportsInstrumentation { get { return !String.IsNullOrEmpty(BeginContextMethodName) && !String.IsNullOrEmpty(EndContextMethodName); } } public override bool Equals(object obj) { if (!(obj is GeneratedClassContext)) { return false; } GeneratedClassContext other = (GeneratedClassContext)obj; return String.Equals(DefineSectionMethodName, other.DefineSectionMethodName, StringComparison.Ordinal) && String.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) && String.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) && String.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) && String.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) && String.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) && String.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) && String.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) && String.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal); } public override int GetHashCode() { // TODO: Use HashCodeCombiner return DefineSectionMethodName.GetHashCode() ^ WriteMethodName.GetHashCode() ^ WriteLiteralMethodName.GetHashCode() ^ WriteToMethodName.GetHashCode() ^ WriteLiteralToMethodName.GetHashCode() ^ ExecuteMethodName.GetHashCode() ^ TemplateTypeName.GetHashCode() ^ BeginContextMethodName.GetHashCode() ^ EndContextMethodName.GetHashCode(); } public static bool operator ==(GeneratedClassContext left, GeneratedClassContext right) { return left.Equals(right); } public static bool operator !=(GeneratedClassContext left, GeneratedClassContext right) { return !left.Equals(right); } } } ================================================ FILE: src/System.Web.Razor/Generator/GeneratedCodeMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public struct GeneratedCodeMapping { public GeneratedCodeMapping(int startLine, int startColumn, int startGeneratedColumn, int codeLength) : this(null, startLine, startColumn, startGeneratedColumn, codeLength) { } public GeneratedCodeMapping(int startOffset, int startLine, int startColumn, int startGeneratedColumn, int codeLength) : this((int?)startOffset, startLine, startColumn, startGeneratedColumn, codeLength) { } private GeneratedCodeMapping(int? startOffset, int startLine, int startColumn, int startGeneratedColumn, int codeLength) : this() { if (startLine < 0) { throw new ArgumentOutOfRangeException("startLine", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "startLine", "0")); } if (startColumn < 0) { throw new ArgumentOutOfRangeException("startColumn", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "startColumn", "0")); } if (startGeneratedColumn < 0) { throw new ArgumentOutOfRangeException("startGeneratedColumn", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "startGeneratedColumn", "0")); } if (codeLength < 0) { throw new ArgumentOutOfRangeException("codeLength", String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Must_Be_GreaterThanOrEqualTo, "codeLength", "0")); } StartOffset = startOffset; StartLine = startLine; StartColumn = startColumn; StartGeneratedColumn = startGeneratedColumn; CodeLength = codeLength; } public int? StartOffset { get; set; } public int CodeLength { get; set; } public int StartColumn { get; set; } public int StartGeneratedColumn { get; set; } public int StartLine { get; set; } public override bool Equals(object obj) { if (!(obj is GeneratedCodeMapping)) { return false; } GeneratedCodeMapping other = (GeneratedCodeMapping)obj; return CodeLength == other.CodeLength && StartColumn == other.StartColumn && StartGeneratedColumn == other.StartGeneratedColumn && StartLine == other.StartLine && // Null means it matches the other no matter what. (StartOffset == null || other.StartOffset == null || StartOffset.Equals(other.StartOffset)); } public override string ToString() { return String.Format( CultureInfo.CurrentCulture, "({0}, {1}, {2}) -> (?, {3}) [{4}]", StartOffset == null ? "?" : StartOffset.Value.ToString(CultureInfo.CurrentCulture), StartLine, StartColumn, StartGeneratedColumn, CodeLength); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(CodeLength) .Add(StartColumn) .Add(StartGeneratedColumn) .Add(StartLine) .Add(StartOffset) .CombinedHash; } public static bool operator ==(GeneratedCodeMapping left, GeneratedCodeMapping right) { return left.Equals(right); } public static bool operator !=(GeneratedCodeMapping left, GeneratedCodeMapping right) { return !left.Equals(right); } } } ================================================ FILE: src/System.Web.Razor/Generator/HelperCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Globalization; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class HelperCodeGenerator : BlockCodeGenerator { private const string HelperWriterName = "__razor_helper_writer"; private CodeWriter _writer; private string _oldWriter; private IDisposable _statementCollectorToken; public HelperCodeGenerator(LocationTagged signature, bool headerComplete) { Signature = signature; HeaderComplete = headerComplete; } public LocationTagged Signature { get; private set; } public LocationTagged Footer { get; set; } public bool HeaderComplete { get; private set; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { _writer = context.CreateCodeWriter(); string prefix = context.BuildCodeString( cw => cw.WriteHelperHeaderPrefix(context.Host.GeneratedClassContext.TemplateTypeName, context.Host.StaticHelpers)); _writer.WriteLinePragma( context.GenerateLinePragma(Signature.Location, prefix.Length, Signature.Value.Length)); _writer.WriteSnippet(prefix); _writer.WriteSnippet(Signature); if (HeaderComplete) { _writer.WriteHelperHeaderSuffix(context.Host.GeneratedClassContext.TemplateTypeName); } _writer.WriteLinePragma(null); if (HeaderComplete) { _writer.WriteReturn(); _writer.WriteStartConstructor(context.Host.GeneratedClassContext.TemplateTypeName); _writer.WriteStartLambdaDelegate(HelperWriterName); } _statementCollectorToken = context.ChangeStatementCollector(AddStatementToHelper); _oldWriter = context.TargetWriterName; context.TargetWriterName = HelperWriterName; } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { _statementCollectorToken.Dispose(); if (HeaderComplete) { _writer.WriteEndLambdaDelegate(); _writer.WriteEndConstructor(); _writer.WriteEndStatement(); } if (Footer != null && !String.IsNullOrEmpty(Footer.Value)) { _writer.WriteLinePragma( context.GenerateLinePragma(Footer.Location, 0, Footer.Value.Length)); _writer.WriteSnippet(Footer); _writer.WriteLinePragma(); } _writer.WriteHelperTrailer(); context.GeneratedClass.Members.Add(new CodeSnippetTypeMember(_writer.Content)); context.TargetWriterName = _oldWriter; } public override bool Equals(object obj) { HelperCodeGenerator other = obj as HelperCodeGenerator; return other != null && base.Equals(other) && HeaderComplete == other.HeaderComplete && Equals(Signature, other.Signature); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(base.GetHashCode()) .Add(Signature) .CombinedHash; } public override string ToString() { return "Helper:" + Signature.ToString("F", CultureInfo.CurrentCulture) + ";" + (HeaderComplete ? "C" : "I"); } private void AddStatementToHelper(string statement, CodeLinePragma pragma) { if (pragma != null) { _writer.WriteLinePragma(pragma); } _writer.WriteSnippet(statement); _writer.InnerWriter.WriteLine(); // CodeDOM normally inserts an extra line so we need to do so here. if (pragma != null) { _writer.WriteLinePragma(); } } } } ================================================ FILE: src/System.Web.Razor/Generator/HybridCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public abstract class HybridCodeGenerator : ISpanCodeGenerator, IBlockCodeGenerator { public virtual void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { } public virtual void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { } public virtual void GenerateCode(Span target, CodeGeneratorContext context) { } } } ================================================ FILE: src/System.Web.Razor/Generator/IBlockCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public interface IBlockCodeGenerator { void GenerateStartBlockCode(Block target, CodeGeneratorContext context); void GenerateEndBlockCode(Block target, CodeGeneratorContext context); } } ================================================ FILE: src/System.Web.Razor/Generator/ISpanCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public interface ISpanCodeGenerator { void GenerateCode(Span target, CodeGeneratorContext context); } } ================================================ FILE: src/System.Web.Razor/Generator/LiteralAttributeCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class LiteralAttributeCodeGenerator : SpanCodeGenerator { public LiteralAttributeCodeGenerator(LocationTagged prefix, LocationTagged valueGenerator) { Prefix = prefix; ValueGenerator = valueGenerator; } public LiteralAttributeCodeGenerator(LocationTagged prefix, LocationTagged value) { Prefix = prefix; Value = value; } public LocationTagged Prefix { get; private set; } public LocationTagged Value { get; private set; } public LocationTagged ValueGenerator { get; private set; } public override void GenerateCode(Span target, CodeGeneratorContext context) { if (context.Host.DesignTimeMode) { return; } ExpressionRenderingMode oldMode = context.ExpressionRenderingMode; context.BufferStatementFragment(context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteStartMethodInvoke("Tuple.Create"); cw.WriteLocationTaggedString(Prefix); cw.WriteParameterSeparator(); if (ValueGenerator != null) { cw.WriteStartMethodInvoke("Tuple.Create", "System.Object", "System.Int32"); context.ExpressionRenderingMode = ExpressionRenderingMode.InjectCode; } else { cw.WriteLocationTaggedString(Value); cw.WriteParameterSeparator(); // literal: true - This attribute value is a literal value cw.WriteBooleanLiteral(true); cw.WriteEndMethodInvoke(); // In VB, we need a line continuation cw.WriteLineContinuation(); } })); if (ValueGenerator != null) { ValueGenerator.Value.GenerateCode(target, context); context.FlushBufferedStatement(); context.ExpressionRenderingMode = oldMode; context.AddStatement(context.BuildCodeString(cw => { cw.WriteParameterSeparator(); cw.WriteSnippet(ValueGenerator.Location.AbsoluteIndex.ToString(CultureInfo.CurrentCulture)); cw.WriteEndMethodInvoke(); cw.WriteParameterSeparator(); // literal: false - This attribute value is not a literal value, it is dynamically generated cw.WriteBooleanLiteral(false); cw.WriteEndMethodInvoke(); // In VB, we need a line continuation cw.WriteLineContinuation(); })); } else { context.FlushBufferedStatement(); } } public override string ToString() { if (ValueGenerator == null) { return String.Format(CultureInfo.CurrentCulture, "LitAttr:{0:F},{1:F}", Prefix, Value); } else { return String.Format(CultureInfo.CurrentCulture, "LitAttr:{0:F},", Prefix, ValueGenerator); } } public override bool Equals(object obj) { LiteralAttributeCodeGenerator other = obj as LiteralAttributeCodeGenerator; return other != null && Equals(other.Prefix, Prefix) && Equals(other.Value, Value) && Equals(other.ValueGenerator, ValueGenerator); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(Prefix) .Add(Value) .Add(ValueGenerator) .CombinedHash; } } } ================================================ FILE: src/System.Web.Razor/Generator/MarkupCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class MarkupCodeGenerator : SpanCodeGenerator { public override void GenerateCode(Span target, CodeGeneratorContext context) { if (!context.Host.DesignTimeMode && String.IsNullOrEmpty(target.Content)) { return; } if (context.Host.EnableInstrumentation) { context.AddContextCall(target, context.Host.GeneratedClassContext.BeginContextMethodName, isLiteral: true); } if (!String.IsNullOrEmpty(target.Content) && !context.Host.DesignTimeMode) { string code = context.BuildCodeString(cw => { if (!String.IsNullOrEmpty(context.TargetWriterName)) { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralToMethodName); cw.WriteSnippet(context.TargetWriterName); cw.WriteParameterSeparator(); } else { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralMethodName); } cw.WriteStringLiteral(target.Content); cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); }); context.AddStatement(code); } if (context.Host.EnableInstrumentation) { context.AddContextCall(target, context.Host.GeneratedClassContext.EndContextMethodName, isLiteral: true); } } public override string ToString() { return "Markup"; } public override bool Equals(object obj) { return obj is MarkupCodeGenerator; } public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/RazorCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Linq; using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public abstract class RazorCodeGenerator : ParserVisitor { private CodeGeneratorContext _context; protected RazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host) { if (String.IsNullOrEmpty(className)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "className"); } if (rootNamespaceName == null) { throw new ArgumentNullException("rootNamespaceName"); } if (host == null) { throw new ArgumentNullException("host"); } ClassName = className; RootNamespaceName = rootNamespaceName; SourceFileName = sourceFileName; GenerateLinePragmas = String.IsNullOrEmpty(SourceFileName) ? false : true; Host = host; } // Data pulled from constructor public string ClassName { get; private set; } public string RootNamespaceName { get; private set; } public string SourceFileName { get; private set; } public RazorEngineHost Host { get; private set; } // Generation settings public bool GenerateLinePragmas { get; set; } public bool DesignTimeMode { get; set; } public CodeGeneratorContext Context { get { EnsureContextInitialized(); return _context; } } internal virtual Func CodeWriterFactory { get { return null; } } public override void VisitStartBlock(Block block) { block.CodeGenerator.GenerateStartBlockCode(block, Context); } public override void VisitEndBlock(Block block) { block.CodeGenerator.GenerateEndBlockCode(block, Context); } public override void VisitSpan(Span span) { span.CodeGenerator.GenerateCode(span, Context); } public override void OnComplete() { Context.FlushBufferedStatement(); } private void EnsureContextInitialized() { if (_context == null) { _context = CodeGeneratorContext.Create(Host, CodeWriterFactory, ClassName, RootNamespaceName, SourceFileName, GenerateLinePragmas); Initialize(_context); } } protected virtual void Initialize(CodeGeneratorContext context) { context.Namespace.Imports.AddRange(Host.NamespaceImports.Select(s => new CodeNamespaceImport(s)).ToArray()); if (!String.IsNullOrEmpty(Host.DefaultBaseClass)) { context.GeneratedClass.BaseTypes.Add(new CodeTypeReference(Host.DefaultBaseClass)); } // Dev10 Bug 937438: Generate explicit Parameter-less constructor on Razor generated class context.GeneratedClass.Members.Add(new CodeConstructor() { Attributes = MemberAttributes.Public }); } } } ================================================ FILE: src/System.Web.Razor/Generator/RazorCommentCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class RazorCommentCodeGenerator : BlockCodeGenerator { public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { // Flush the buffered statement since we're interrupting it with a comment. if (!String.IsNullOrEmpty(context.CurrentBufferedStatement)) { context.MarkEndOfGeneratedCode(); context.BufferStatementFragment(context.BuildCodeString(cw => cw.WriteLineContinuation())); } context.FlushBufferedStatement(); } } } ================================================ FILE: src/System.Web.Razor/Generator/RazorDirectiveAttributeCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.Razor.Parser.SyntaxTree; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class RazorDirectiveAttributeCodeGenerator : SpanCodeGenerator { public RazorDirectiveAttributeCodeGenerator(string name, string value) { if (String.IsNullOrEmpty(name)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "name"); } Name = name; Value = value ?? String.Empty; // Coerce to empty string if it was null. } public string Name { get; private set; } public string Value { get; private set; } public override void GenerateCode(Span target, CodeGeneratorContext context) { var attributeType = new CodeTypeReference(typeof(RazorDirectiveAttribute)); var attributeDeclaration = new CodeAttributeDeclaration( attributeType, new CodeAttributeArgument(new CodePrimitiveExpression(Name)), new CodeAttributeArgument(new CodePrimitiveExpression(Value))); context.GeneratedClass.CustomAttributes.Add(attributeDeclaration); } public override string ToString() { return "Directive: " + Name + ", Value: " + Value; } public override bool Equals(object obj) { RazorDirectiveAttributeCodeGenerator other = obj as RazorDirectiveAttributeCodeGenerator; return other != null && Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && Value.Equals(other.Value, StringComparison.OrdinalIgnoreCase); } public override int GetHashCode() { return Tuple.Create(Name.ToUpperInvariant(), Value.ToUpperInvariant()) .GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/ResolveUrlCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class ResolveUrlCodeGenerator : SpanCodeGenerator { public override void GenerateCode(Span target, CodeGeneratorContext context) { // Check if the host supports it if (String.IsNullOrEmpty(context.Host.GeneratedClassContext.ResolveUrlMethodName)) { // Nope, just use the default MarkupCodeGenerator behavior new MarkupCodeGenerator().GenerateCode(target, context); return; } if (!context.Host.DesignTimeMode && String.IsNullOrEmpty(target.Content)) { return; } if (context.Host.EnableInstrumentation && context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { // Add a non-literal context call (non-literal because the expanded URL will not match the source character-by-character) context.AddContextCall(target, context.Host.GeneratedClassContext.BeginContextMethodName, isLiteral: false); } if (!String.IsNullOrEmpty(target.Content) && !context.Host.DesignTimeMode) { string code = context.BuildCodeString(cw => { if (context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { if (!String.IsNullOrEmpty(context.TargetWriterName)) { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralToMethodName); cw.WriteSnippet(context.TargetWriterName); cw.WriteParameterSeparator(); } else { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.WriteLiteralMethodName); } } cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.ResolveUrlMethodName); cw.WriteStringLiteral(target.Content); cw.WriteEndMethodInvoke(); if (context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); } else { cw.WriteLineContinuation(); } }); if (context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { context.AddStatement(code); } else { context.BufferStatementFragment(code); } } if (context.Host.EnableInstrumentation && context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) { context.AddContextCall(target, context.Host.GeneratedClassContext.EndContextMethodName, isLiteral: false); } } public override string ToString() { return "VirtualPath"; } public override bool Equals(object obj) { return obj is ResolveUrlCodeGenerator; } public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/SectionCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; using Microsoft.Internal.Web.Utils; namespace System.Web.Razor.Generator { public class SectionCodeGenerator : BlockCodeGenerator { public SectionCodeGenerator(string sectionName) { SectionName = sectionName; } public string SectionName { get; private set; } public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { string startBlock = context.BuildCodeString(cw => { cw.WriteStartMethodInvoke(context.Host.GeneratedClassContext.DefineSectionMethodName); cw.WriteStringLiteral(SectionName); cw.WriteParameterSeparator(); cw.WriteStartLambdaDelegate(); }); context.AddStatement(startBlock); } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { string startBlock = context.BuildCodeString(cw => { cw.WriteEndLambdaDelegate(); cw.WriteEndMethodInvoke(); cw.WriteEndStatement(); }); context.AddStatement(startBlock); } public override bool Equals(object obj) { SectionCodeGenerator other = obj as SectionCodeGenerator; return other != null && base.Equals(other) && String.Equals(SectionName, other.SectionName, StringComparison.Ordinal); } public override int GetHashCode() { return HashCodeCombiner.Start() .Add(base.GetHashCode()) .Add(SectionName) .CombinedHash; } public override string ToString() { return "Section:" + SectionName; } } } ================================================ FILE: src/System.Web.Razor/Generator/SetBaseTypeCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class SetBaseTypeCodeGenerator : SpanCodeGenerator { public SetBaseTypeCodeGenerator(string baseType) { BaseType = baseType; } public string BaseType { get; private set; } public override void GenerateCode(Span target, CodeGeneratorContext context) { context.GeneratedClass.BaseTypes.Clear(); context.GeneratedClass.BaseTypes.Add(new CodeTypeReference(ResolveType(context, BaseType.Trim()))); if (context.Host.DesignTimeMode) { int generatedCodeStart = 0; string code = context.BuildCodeString(cw => { generatedCodeStart = cw.WriteVariableDeclaration(target.Content, "__inheritsHelper", null); cw.WriteEndStatement(); }); int paddingCharCount; CodeSnippetStatement stmt = new CodeSnippetStatement( CodeGeneratorPaddingHelper.Pad(context.Host, code, target, generatedCodeStart, out paddingCharCount)) { LinePragma = context.GenerateLinePragma(target, generatedCodeStart + paddingCharCount) }; context.AddDesignTimeHelperStatement(stmt); } } protected virtual string ResolveType(CodeGeneratorContext context, string baseType) { return baseType; } public override string ToString() { return "Base:" + BaseType; } public override bool Equals(object obj) { SetBaseTypeCodeGenerator other = obj as SetBaseTypeCodeGenerator; return other != null && String.Equals(BaseType, other.BaseType, StringComparison.Ordinal); } public override int GetHashCode() { return BaseType.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/SetLayoutCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class SetLayoutCodeGenerator : SpanCodeGenerator { public SetLayoutCodeGenerator(string layoutPath) { LayoutPath = layoutPath; } public string LayoutPath { get; set; } public override void GenerateCode(Span target, CodeGeneratorContext context) { if (!context.Host.DesignTimeMode && !String.IsNullOrEmpty(context.Host.GeneratedClassContext.LayoutPropertyName)) { context.TargetMethod.Statements.Add( new CodeAssignStatement( new CodePropertyReferenceExpression(null, context.Host.GeneratedClassContext.LayoutPropertyName), new CodePrimitiveExpression(LayoutPath))); } } public override string ToString() { return "Layout: " + LayoutPath; } public override bool Equals(object obj) { SetLayoutCodeGenerator other = obj as SetLayoutCodeGenerator; return other != null && String.Equals(other.LayoutPath, LayoutPath, StringComparison.Ordinal); } public override int GetHashCode() { return LayoutPath.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/SetVBOptionCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class SetVBOptionCodeGenerator : SpanCodeGenerator { public static readonly string StrictCodeDomOptionName = "AllowLateBound"; public static readonly string ExplicitCodeDomOptionName = "RequireVariableDeclaration"; public SetVBOptionCodeGenerator(string optionName, bool value) { OptionName = optionName; Value = value; } // CodeDOM Option Name, which is NOT the same as the VB Option Name public string OptionName { get; private set; } public bool Value { get; private set; } public static SetVBOptionCodeGenerator Strict(bool onOffValue) { // Strict On = AllowLateBound Off return new SetVBOptionCodeGenerator(StrictCodeDomOptionName, !onOffValue); } public static SetVBOptionCodeGenerator Explicit(bool onOffValue) { return new SetVBOptionCodeGenerator(ExplicitCodeDomOptionName, onOffValue); } public override void GenerateCode(Span target, CodeGeneratorContext context) { context.CompileUnit.UserData[OptionName] = Value; } public override string ToString() { return "Option:" + OptionName + "=" + Value; } } } ================================================ FILE: src/System.Web.Razor/Generator/SpanCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public abstract class SpanCodeGenerator : ISpanCodeGenerator { [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "This class has no instance state")] public static readonly ISpanCodeGenerator Null = new NullSpanCodeGenerator(); public virtual void GenerateCode(Span target, CodeGeneratorContext context) { } public override bool Equals(object obj) { return (obj as ISpanCodeGenerator) != null; } public override int GetHashCode() { return base.GetHashCode(); } private class NullSpanCodeGenerator : ISpanCodeGenerator { public void GenerateCode(Span target, CodeGeneratorContext context) { } public override string ToString() { return "None"; } } } } ================================================ FILE: src/System.Web.Razor/Generator/StatementCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class StatementCodeGenerator : SpanCodeGenerator { public override void GenerateCode(Span target, CodeGeneratorContext context) { context.FlushBufferedStatement(); string generatedCode = context.BuildCodeString(cw => { cw.WriteSnippet(target.Content); }); int startGeneratedCode = target.Start.CharacterIndex; int paddingCharCount; generatedCode = CodeGeneratorPaddingHelper.PadStatement(context.Host, generatedCode, target, ref startGeneratedCode, out paddingCharCount); context.AddStatement( generatedCode, context.GenerateLinePragma(target, paddingCharCount)); } public override string ToString() { return "Stmt"; } public override bool Equals(object obj) { return obj is StatementCodeGenerator; } public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/TemplateBlockCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class TemplateBlockCodeGenerator : BlockCodeGenerator { private const string TemplateWriterName = "__razor_template_writer"; private const string ItemParameterName = "item"; private string _oldTargetWriter; public override void GenerateStartBlockCode(Block target, CodeGeneratorContext context) { string generatedCode = context.BuildCodeString(cw => { cw.WriteStartLambdaExpression(ItemParameterName); cw.WriteStartConstructor(context.Host.GeneratedClassContext.TemplateTypeName); cw.WriteStartLambdaDelegate(TemplateWriterName); }); context.MarkEndOfGeneratedCode(); context.BufferStatementFragment(generatedCode); context.FlushBufferedStatement(); _oldTargetWriter = context.TargetWriterName; context.TargetWriterName = TemplateWriterName; } public override void GenerateEndBlockCode(Block target, CodeGeneratorContext context) { string generatedCode = context.BuildCodeString(cw => { cw.WriteEndLambdaDelegate(); cw.WriteEndConstructor(); cw.WriteEndLambdaExpression(); }); context.BufferStatementFragment(generatedCode); context.TargetWriterName = _oldTargetWriter; } } } ================================================ FILE: src/System.Web.Razor/Generator/TypeMemberCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Diagnostics.Contracts; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Generator { public class TypeMemberCodeGenerator : SpanCodeGenerator { public override void GenerateCode(Span target, CodeGeneratorContext context) { string generatedCode = context.BuildCodeString(cw => { cw.WriteSnippet(target.Content); }); int paddingCharCount; string paddedCode = CodeGeneratorPaddingHelper.Pad(context.Host, generatedCode, target, out paddingCharCount); Contract.Assert(paddingCharCount > 0); context.GeneratedClass.Members.Add( new CodeSnippetTypeMember(paddedCode) { LinePragma = context.GenerateLinePragma(target, paddingCharCount) }); } public override string ToString() { return "TypeMember"; } public override bool Equals(object obj) { return obj is TypeMemberCodeGenerator; } // C# complains at us if we don't provide an implementation, even one like this public override int GetHashCode() { return base.GetHashCode(); } } } ================================================ FILE: src/System.Web.Razor/Generator/VBCodeWriter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Generator { internal class VBCodeWriter : BaseCodeWriter { public override bool SupportsMidStatementLinePragmas { get { return false; } } protected internal override void WriteStartGenerics() { InnerWriter.Write("(Of "); } protected internal override void WriteEndGenerics() { InnerWriter.Write(")"); } public override void WriteLineContinuation() { InnerWriter.Write(" _"); } public override int WriteVariableDeclaration(string type, string name, string value) { InnerWriter.Write("Dim "); InnerWriter.Write(name); InnerWriter.Write(" As "); int typePos = InnerWriter.GetStringBuilder().Length; InnerWriter.Write(type); if (!String.IsNullOrEmpty(value)) { InnerWriter.Write(" = "); InnerWriter.Write(value); } else { InnerWriter.Write(" = Nothing"); } return typePos; } public override void WriteStringLiteral(string literal) { bool inQuotes = true; InnerWriter.Write("\""); for (int i = 0; i < literal.Length; i++) { switch (literal[i]) { case '\t': case '\n': case '\r': case '\0': case '\u2028': case '\u2029': // Exit quotes EnsureOutOfQuotes(ref inQuotes); // Write concat character InnerWriter.Write("&"); // Write character literal WriteCharLiteral(literal[i]); break; case '"': case '“': case '”': case (char)0xff02: EnsureInQuotes(ref inQuotes); InnerWriter.Write(literal[i]); InnerWriter.Write(literal[i]); break; default: EnsureInQuotes(ref inQuotes); InnerWriter.Write(literal[i]); break; } if (i > 0 && (i % 80) == 0) { if ((Char.IsHighSurrogate(literal[i]) && (i < (literal.Length - 1))) && Char.IsLowSurrogate(literal[i + 1])) { InnerWriter.Write(literal[++i]); } if (inQuotes) { InnerWriter.Write("\""); } inQuotes = true; InnerWriter.Write("& _ "); InnerWriter.Write(Environment.NewLine); InnerWriter.Write('"'); } } EnsureOutOfQuotes(ref inQuotes); } protected internal override void EmitStartLambdaExpression(string[] parameterNames) { InnerWriter.Write("Function ("); WriteCommaSeparatedList(parameterNames, InnerWriter.Write); InnerWriter.Write(") "); } protected internal override void EmitStartConstructor(string typeName) { InnerWriter.Write("New "); InnerWriter.Write(typeName); InnerWriter.Write("("); } protected internal override void EmitStartLambdaDelegate(string[] parameterNames) { InnerWriter.Write("Sub ("); WriteCommaSeparatedList(parameterNames, InnerWriter.Write); InnerWriter.WriteLine(")"); } protected internal override void EmitEndLambdaDelegate() { InnerWriter.Write("End Sub"); } private void WriteCharLiteral(char literal) { InnerWriter.Write("Global.Microsoft.VisualBasic.ChrW("); InnerWriter.Write((int)literal); InnerWriter.Write(")"); } private void EnsureInQuotes(ref bool inQuotes) { if (!inQuotes) { InnerWriter.Write("&\""); inQuotes = true; } } private void EnsureOutOfQuotes(ref bool inQuotes) { if (inQuotes) { InnerWriter.Write("\""); inQuotes = false; } } public override void WriteReturn() { InnerWriter.Write("Return "); } public override void WriteLinePragma(int? lineNumber, string fileName) { InnerWriter.WriteLine(); if (lineNumber != null) { InnerWriter.Write("#ExternalSource(\""); InnerWriter.Write(fileName); InnerWriter.Write("\", "); InnerWriter.Write(lineNumber); InnerWriter.WriteLine(")"); } else { InnerWriter.WriteLine("#End ExternalSource"); } } public override void WriteHelperHeaderPrefix(string templateTypeName, bool isStatic) { InnerWriter.Write("Public "); if (isStatic) { InnerWriter.Write("Shared "); } InnerWriter.Write("Function "); } public override void WriteHelperHeaderSuffix(string templateTypeName) { InnerWriter.Write(" As "); InnerWriter.WriteLine(templateTypeName); } public override void WriteHelperTrailer() { InnerWriter.WriteLine("End Function"); } public override void WriteEndStatement() { InnerWriter.WriteLine(); } } } ================================================ FILE: src/System.Web.Razor/Generator/VBRazorCodeGenerator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Generator { public class VBRazorCodeGenerator : RazorCodeGenerator { public VBRazorCodeGenerator(string className, string rootNamespaceName, string sourceFileName, RazorEngineHost host) : base(className, rootNamespaceName, sourceFileName, host) { } internal override Func CodeWriterFactory { get { return () => new VBCodeWriter(); } } } } ================================================ FILE: src/System.Web.Razor/GeneratorResults.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Collections.Generic; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor { /// /// Represents results from code generation (and parsing, since that is a pre-requisite of code generation) /// /// /// Since this inherits from ParserResults, it has all the data from ParserResults, and simply adds code generation data /// public class GeneratorResults : ParserResults { public GeneratorResults(ParserResults parserResults, CodeCompileUnit generatedCode, IDictionary designTimeLineMappings) : this(parserResults.Document, parserResults.ParserErrors, generatedCode, designTimeLineMappings) { } public GeneratorResults(Block document, IList parserErrors, CodeCompileUnit generatedCode, IDictionary designTimeLineMappings) : this(parserErrors.Count == 0, document, parserErrors, generatedCode, designTimeLineMappings) { } protected GeneratorResults(bool success, Block document, IList parserErrors, CodeCompileUnit generatedCode, IDictionary designTimeLineMappings) : base(success, document, parserErrors) { GeneratedCode = generatedCode; DesignTimeLineMappings = designTimeLineMappings; } /// /// The generated code /// public CodeCompileUnit GeneratedCode { get; private set; } /// /// If design-time mode was used in the Code Generator, this will contain the dictionary /// of design-time generated code mappings /// public IDictionary DesignTimeLineMappings { get; private set; } } } ================================================ FILE: src/System.Web.Razor/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "br", Scope = "resource", Target = "System.Web.Razor.Resources.RazorResources.resources", Justification = "Resource is referencing html tag")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor.Tokenizer.Symbols", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor.Tokenizer", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor.Text", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor.Parser", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor.Editor", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.Razor", Justification = "These namespaces are design to group classes by function. They will be reviewed to ensure they remain relevant.")] [assembly: SuppressMessage("Microsoft.Web.FxCop", "MW1000:UnusedResourceUsageRule", Justification = "There are numerous unused resources due to VB being disabled. This rule will be re-run after VB is restored")] ================================================ FILE: src/System.Web.Razor/Parser/BalancingModes.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.Razor.Parser { [Flags] public enum BalancingModes { None = 0, BacktrackOnFailure = 1, NoErrorOnFailure = 2, AllowCommentsAndTemplates = 4, AllowEmbeddedTransitions = 8 } } ================================================ FILE: src/System.Web.Razor/Parser/CSharpCodeParser.Directives.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Web.Razor.Editor; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class CSharpCodeParser { private void SetupDirectives() { MapDirectives(InheritsDirective, SyntaxConstants.CSharp.InheritsKeyword); MapDirectives(FunctionsDirective, SyntaxConstants.CSharp.FunctionsKeyword); MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword); MapDirectives(HelperDirective, SyntaxConstants.CSharp.HelperKeyword); MapDirectives(LayoutDirective, SyntaxConstants.CSharp.LayoutKeyword); MapDirectives(SessionStateDirective, SyntaxConstants.CSharp.SessionStateKeyword); } protected virtual void LayoutDirective() { AssertDirective(SyntaxConstants.CSharp.LayoutKeyword); AcceptAndMoveNext(); Context.CurrentBlock.Type = BlockType.Directive; // Accept spaces, but not newlines bool foundSomeWhitespace = At(CSharpSymbolType.WhiteSpace); AcceptWhile(CSharpSymbolType.WhiteSpace); Output(SpanKind.MetaCode, foundSomeWhitespace ? AcceptedCharacters.None : AcceptedCharacters.Any); // First non-whitespace character starts the Layout Page, then newline ends it AcceptUntil(CSharpSymbolType.NewLine); Span.CodeGenerator = new SetLayoutCodeGenerator(Span.GetContent()); Span.EditHandler.EditorHints = EditorHints.LayoutPage | EditorHints.VirtualPath; bool foundNewline = Optional(CSharpSymbolType.NewLine); AddMarkerSymbolIfNecessary(); Output(SpanKind.MetaCode, foundNewline ? AcceptedCharacters.None : AcceptedCharacters.Any); } protected virtual void SessionStateDirective() { AssertDirective(SyntaxConstants.CSharp.SessionStateKeyword); AcceptAndMoveNext(); SessionStateDirectiveCore(); } protected void SessionStateDirectiveCore() { SessionStateTypeDirective(RazorResources.ParserEror_SessionDirectiveMissingValue, (key, value) => new RazorDirectiveAttributeCodeGenerator(key, value)); } protected void SessionStateTypeDirective(string noValueError, Func createCodeGenerator) { // Set the block type Context.CurrentBlock.Type = BlockType.Directive; // Accept whitespace CSharpSymbol remainingWs = AcceptSingleWhiteSpaceCharacter(); if (Span.Symbols.Count > 1) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } Output(SpanKind.MetaCode); if (remainingWs != null) { Accept(remainingWs); } AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); // Parse a Type Name if (!ValidSessionStateValue()) { Context.OnError(CurrentLocation, noValueError); } // Pull out the type name string sessionStateValue = String.Concat( Span.Symbols .Cast() .Select(sym => sym.Content)).Trim(); // Set up code generation Span.CodeGenerator = createCodeGenerator(SyntaxConstants.CSharp.SessionStateKeyword, sessionStateValue); // Output the span and finish the block CompleteBlock(); Output(SpanKind.Code); } protected virtual bool ValidSessionStateValue() { return Optional(CSharpSymbolType.Identifier); } [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Coupling will be reviewed at a later date")] [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "C# Keywords are always lower-case")] protected virtual void HelperDirective() { bool nested = Context.IsWithin(BlockType.Helper); // Set the block and span type Context.CurrentBlock.Type = BlockType.Helper; // Verify we're on "helper" and accept AssertDirective(SyntaxConstants.CSharp.HelperKeyword); Block block = new Block(CurrentSymbol.Content.ToString().ToLowerInvariant(), CurrentLocation); AcceptAndMoveNext(); if (nested) { Context.OnError(CurrentLocation, RazorResources.ParseError_Helpers_Cannot_Be_Nested); } // Accept a single whitespace character if present, if not, we should stop now if (!At(CSharpSymbolType.WhiteSpace)) { string error; if (At(CSharpSymbolType.NewLine)) { error = RazorResources.ErrorComponent_Newline; } else if (EndOfFile) { error = RazorResources.ErrorComponent_EndOfFile; } else { error = String.Format(CultureInfo.CurrentCulture, RazorResources.ErrorComponent_Character, CurrentSymbol.Content); } Context.OnError( CurrentLocation, RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, error); PutCurrentBack(); Output(SpanKind.MetaCode); return; } CSharpSymbol remainingWs = AcceptSingleWhiteSpaceCharacter(); // Output metacode and continue Output(SpanKind.MetaCode); if (remainingWs != null) { Accept(remainingWs); } AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); // Don't accept newlines. // Expecting an identifier (helper name) bool errorReported = !Required(CSharpSymbolType.Identifier, errorIfNotFound: true, errorBase: RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start); if (!errorReported) { Assert(CSharpSymbolType.Identifier); AcceptAndMoveNext(); } AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); // Expecting parameter list start: "(" SourceLocation bracketErrorPos = CurrentLocation; if (!Optional(CSharpSymbolType.LeftParenthesis)) { if (!errorReported) { errorReported = true; Context.OnError( CurrentLocation, RazorResources.ParseError_MissingCharAfterHelperName, "("); } } else { SourceLocation bracketStart = CurrentLocation; if (!Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftParenthesis, CSharpSymbolType.RightParenthesis, bracketStart)) { errorReported = true; Context.OnError( bracketErrorPos, RazorResources.ParseError_UnterminatedHelperParameterList); } Optional(CSharpSymbolType.RightParenthesis); } int bookmark = CurrentLocation.AbsoluteIndex; IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); // Expecting a "{" SourceLocation errorLocation = CurrentLocation; bool headerComplete = At(CSharpSymbolType.LeftBrace); if (headerComplete) { Accept(ws); AcceptAndMoveNext(); } else { Context.Source.Position = bookmark; NextToken(); AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); if (!errorReported) { Context.OnError( errorLocation, RazorResources.ParseError_MissingCharAfterHelperParameters, Language.GetSample(CSharpSymbolType.LeftBrace)); } } // Grab the signature and build the code generator AddMarkerSymbolIfNecessary(); LocationTagged signature = Span.GetContent(); HelperCodeGenerator blockGen = new HelperCodeGenerator(signature, headerComplete); Context.CurrentBlock.CodeGenerator = blockGen; // The block will generate appropriate code, Span.CodeGenerator = SpanCodeGenerator.Null; if (!headerComplete) { CompleteBlock(); Output(SpanKind.Code); return; } else { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Output(SpanKind.Code); } // We're valid, so parse the nested block AutoCompleteEditHandler bodyEditHandler = new AutoCompleteEditHandler(Language.TokenizeString); using (PushSpanConfig(DefaultSpanConfig)) { using (Context.StartBlock(BlockType.Statement)) { Span.EditHandler = bodyEditHandler; CodeBlock(false, block); CompleteBlock(insertMarkerIfNecessary: true); Output(SpanKind.Code); } } Initialize(Span); EnsureCurrent(); Span.CodeGenerator = SpanCodeGenerator.Null; // The block will generate the footer code. if (!Optional(CSharpSymbolType.RightBrace)) { // The } is missing, so set the initial signature span to use it as an autocomplete string bodyEditHandler.AutoCompleteString = "}"; // Need to be able to accept anything to properly handle the autocomplete bodyEditHandler.AcceptedCharacters = AcceptedCharacters.Any; } else { blockGen.Footer = Span.GetContent(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } CompleteBlock(); Output(SpanKind.Code); } protected virtual void SectionDirective() { bool nested = Context.IsWithin(BlockType.Section); bool errorReported = false; // Set the block and span type Context.CurrentBlock.Type = BlockType.Section; // Verify we're on "section" and accept AssertDirective(SyntaxConstants.CSharp.SectionKeyword); AcceptAndMoveNext(); if (nested) { Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_Sections_Cannot_Be_Nested, RazorResources.SectionExample_CS)); errorReported = true; } IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); // Get the section name string sectionName = String.Empty; if (!Required(CSharpSymbolType.Identifier, errorIfNotFound: true, errorBase: RazorResources.ParseError_Unexpected_Character_At_Section_Name_Start)) { if (!errorReported) { errorReported = true; } PutCurrentBack(); PutBack(ws); AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false)); } else { Accept(ws); sectionName = CurrentSymbol.Content; AcceptAndMoveNext(); } Context.CurrentBlock.CodeGenerator = new SectionCodeGenerator(sectionName); SourceLocation errorLocation = CurrentLocation; ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); // Get the starting brace bool sawStartingBrace = At(CSharpSymbolType.LeftBrace); if (!sawStartingBrace) { if (!errorReported) { errorReported = true; Context.OnError(errorLocation, RazorResources.ParseError_MissingOpenBraceAfterSection); } PutCurrentBack(); PutBack(ws); AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false)); Optional(CSharpSymbolType.NewLine); Output(SpanKind.MetaCode); CompleteBlock(); return; } else { Accept(ws); } // Set up edit handler AutoCompleteEditHandler editHandler = new AutoCompleteEditHandler(Language.TokenizeString) { AutoCompleteAtEndOfSpan = true }; Span.EditHandler = editHandler; Span.Accept(CurrentSymbol); // Output Metacode then switch to section parser Output(SpanKind.MetaCode); SectionBlock("{", "}", caseSensitive: true); Span.CodeGenerator = SpanCodeGenerator.Null; // Check for the terminating "}" if (!Optional(CSharpSymbolType.RightBrace)) { editHandler.AutoCompleteString = "}"; Context.OnError(CurrentLocation, RazorResources.ParseError_Expected_X, Language.GetSample(CSharpSymbolType.RightBrace)); } else { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } CompleteBlock(insertMarkerIfNecessary: false, captureWhitespaceToEndOfLine: true); Output(SpanKind.MetaCode); return; } protected virtual void FunctionsDirective() { // Set the block type Context.CurrentBlock.Type = BlockType.Functions; // Verify we're on "functions" and accept AssertDirective(SyntaxConstants.CSharp.FunctionsKeyword); Block block = new Block(CurrentSymbol); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); if (!At(CSharpSymbolType.LeftBrace)) { Context.OnError(CurrentLocation, RazorResources.ParseError_Expected_X, Language.GetSample(CSharpSymbolType.LeftBrace)); CompleteBlock(); Output(SpanKind.MetaCode); return; } else { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } // Capture start point and continue SourceLocation blockStart = CurrentLocation; AcceptAndMoveNext(); // Output what we've seen and continue Output(SpanKind.MetaCode); AutoCompleteEditHandler editHandler = new AutoCompleteEditHandler(Language.TokenizeString); Span.EditHandler = editHandler; Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, blockStart); Span.CodeGenerator = new TypeMemberCodeGenerator(); if (!At(CSharpSymbolType.RightBrace)) { editHandler.AutoCompleteString = "}"; Context.OnError(block.Start, RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, block.Name, "}", "{"); CompleteBlock(); Output(SpanKind.Code); } else { Output(SpanKind.Code); Assert(CSharpSymbolType.RightBrace); Span.CodeGenerator = SpanCodeGenerator.Null; Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; AcceptAndMoveNext(); CompleteBlock(); Output(SpanKind.MetaCode); } } protected virtual void InheritsDirective() { // Verify we're on the right keyword and accept AssertDirective(SyntaxConstants.CSharp.InheritsKeyword); AcceptAndMoveNext(); InheritsDirectiveCore(); } [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "directive", Justification = "This only occurs in Release builds, where this method is empty by design")] [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This only occurs in Release builds, where this method is empty by design")] [Conditional("DEBUG")] protected void AssertDirective(string directive) { Assert(CSharpSymbolType.Identifier); Debug.Assert(String.Equals(CurrentSymbol.Content, directive, StringComparison.Ordinal)); } protected void InheritsDirectiveCore() { BaseTypeDirective(RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, baseType => new SetBaseTypeCodeGenerator(baseType)); } protected void BaseTypeDirective(string noTypeNameError, Func createCodeGenerator) { // Set the block type Context.CurrentBlock.Type = BlockType.Directive; // Accept whitespace CSharpSymbol remainingWs = AcceptSingleWhiteSpaceCharacter(); if (Span.Symbols.Count > 1) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } Output(SpanKind.MetaCode); if (remainingWs != null) { Accept(remainingWs); } AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); if (EndOfFile || At(CSharpSymbolType.WhiteSpace) || At(CSharpSymbolType.NewLine)) { Context.OnError(CurrentLocation, noTypeNameError); } // Parse to the end of the line AcceptUntil(CSharpSymbolType.NewLine); if (!Context.DesignTimeMode) { // We want the newline to be treated as code, but it causes issues at design-time. Optional(CSharpSymbolType.NewLine); } // Pull out the type name string baseType = Span.GetContent(); // Set up code generation Span.CodeGenerator = createCodeGenerator(baseType.Trim()); // Output the span and finish the block CompleteBlock(); Output(SpanKind.Code); } } } ================================================ FILE: src/System.Web.Razor/Parser/CSharpCodeParser.Statements.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class CSharpCodeParser { private void SetUpKeywords() { MapKeywords(ConditionalBlock, CSharpKeyword.For, CSharpKeyword.Foreach, CSharpKeyword.While, CSharpKeyword.Switch, CSharpKeyword.Lock); MapKeywords(CaseStatement, false, CSharpKeyword.Case, CSharpKeyword.Default); MapKeywords(IfStatement, CSharpKeyword.If); MapKeywords(TryStatement, CSharpKeyword.Try); MapKeywords(UsingKeyword, CSharpKeyword.Using); MapKeywords(DoStatement, CSharpKeyword.Do); MapKeywords(ReservedDirective, CSharpKeyword.Namespace, CSharpKeyword.Class); } protected virtual void ReservedDirective(bool topLevel) { Context.OnError(CurrentLocation, String.Format(CultureInfo.CurrentCulture, RazorResources.ParseError_ReservedWord, CurrentSymbol.Content)); AcceptAndMoveNext(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; Context.CurrentBlock.Type = BlockType.Directive; CompleteBlock(); Output(SpanKind.MetaCode); } private void KeywordBlock(bool topLevel) { HandleKeyword(topLevel, () => { Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); ImplicitExpression(); }); } private void CaseStatement(bool topLevel) { Assert(CSharpSymbolType.Keyword); Debug.Assert(CurrentSymbol.Keyword != null && (CurrentSymbol.Keyword.Value == CSharpKeyword.Case || CurrentSymbol.Keyword.Value == CSharpKeyword.Default)); AcceptUntil(CSharpSymbolType.Colon); Optional(CSharpSymbolType.Colon); } private void DoStatement(bool topLevel) { Assert(CSharpKeyword.Do); UnconditionalBlock(); WhileClause(); if (topLevel) { CompleteBlock(); } } private void WhileClause() { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; IEnumerable ws = SkipToNextImportantToken(); if (At(CSharpKeyword.While)) { Accept(ws); Assert(CSharpKeyword.While); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); if (AcceptCondition() && Optional(CSharpSymbolType.Semicolon)) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } } else { PutCurrentBack(); PutBack(ws); } } private void UsingKeyword(bool topLevel) { Assert(CSharpKeyword.Using); Block block = new Block(CurrentSymbol); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); if (At(CSharpSymbolType.LeftParenthesis)) { // using ( ==> Using Statement UsingStatement(block); } else if (At(CSharpSymbolType.Identifier)) { // using Identifier ==> Using Declaration if (!topLevel) { Context.OnError(block.Start, RazorResources.ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock); StandardStatement(); } else { UsingDeclaration(); } } if (topLevel) { CompleteBlock(); } } private void UsingDeclaration() { // Set block type to directive Context.CurrentBlock.Type = BlockType.Directive; // Parse a type name Assert(CSharpSymbolType.Identifier); NamespaceOrTypeName(); IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); if (At(CSharpSymbolType.Assign)) { // Alias Accept(ws); Assert(CSharpSymbolType.Assign); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); // One more namespace or type name NamespaceOrTypeName(); } else { PutCurrentBack(); PutBack(ws); } Span.EditHandler.AcceptedCharacters = AcceptedCharacters.AnyExceptNewline; Span.CodeGenerator = new AddImportCodeGenerator( Span.GetContent(syms => syms.Skip(1)), // Skip "using" SyntaxConstants.CSharp.UsingKeywordLength); // Optional ";" if (EnsureCurrent()) { Optional(CSharpSymbolType.Semicolon); } } private bool NamespaceOrTypeName() { if (Optional(CSharpSymbolType.Identifier) || Optional(CSharpSymbolType.Keyword)) { Optional(CSharpSymbolType.QuestionMark); // Nullable if (Optional(CSharpSymbolType.DoubleColon)) { if (!Optional(CSharpSymbolType.Identifier)) { Optional(CSharpSymbolType.Keyword); } } if (At(CSharpSymbolType.LessThan)) { TypeArgumentList(); } if (Optional(CSharpSymbolType.Dot)) { NamespaceOrTypeName(); } while (At(CSharpSymbolType.LeftBracket)) { Balance(BalancingModes.None); Optional(CSharpSymbolType.RightBracket); } return true; } else { return false; } } private void TypeArgumentList() { Assert(CSharpSymbolType.LessThan); Balance(BalancingModes.None); Optional(CSharpSymbolType.GreaterThan); } private void UsingStatement(Block block) { Assert(CSharpSymbolType.LeftParenthesis); // Parse condition if (AcceptCondition()) { AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); // Parse code block ExpectCodeBlock(block); } } private void TryStatement(bool topLevel) { Assert(CSharpKeyword.Try); UnconditionalBlock(); AfterTryClause(); if (topLevel) { CompleteBlock(); } } private void IfStatement(bool topLevel) { Assert(CSharpKeyword.If); ConditionalBlock(topLevel: false); AfterIfClause(); if (topLevel) { CompleteBlock(); } } private void AfterTryClause() { // Grab whitespace IEnumerable ws = SkipToNextImportantToken(); // Check for a catch or finally part if (At(CSharpKeyword.Catch)) { Accept(ws); Assert(CSharpKeyword.Catch); ConditionalBlock(topLevel: false); AfterTryClause(); } else if (At(CSharpKeyword.Finally)) { Accept(ws); Assert(CSharpKeyword.Finally); UnconditionalBlock(); } else { // Return whitespace and end the block PutCurrentBack(); PutBack(ws); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; } } private void AfterIfClause() { // Grab whitespace and razor comments IEnumerable ws = SkipToNextImportantToken(); // Check for an else part if (At(CSharpKeyword.Else)) { Accept(ws); Assert(CSharpKeyword.Else); ElseClause(); } else { // No else, return whitespace PutCurrentBack(); PutBack(ws); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; } } private void ElseClause() { if (!At(CSharpKeyword.Else)) { return; } Block block = new Block(CurrentSymbol); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); if (At(CSharpKeyword.If)) { // ElseIf block.Name = SyntaxConstants.CSharp.ElseIfKeyword; ConditionalBlock(block); AfterIfClause(); } else if (!EndOfFile) { // Else ExpectCodeBlock(block); } } private void ExpectCodeBlock(Block block) { if (!EndOfFile) { // Check for "{" to make sure we're at a block if (!At(CSharpSymbolType.LeftBrace)) { Context.OnError(CurrentLocation, RazorResources.ParseError_SingleLine_ControlFlowStatements_Not_Allowed, Language.GetSample(CSharpSymbolType.LeftBrace), CurrentSymbol.Content); } // Parse the statement and then we're done Statement(block); } } private void UnconditionalBlock() { Assert(CSharpSymbolType.Keyword); Block block = new Block(CurrentSymbol); AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); ExpectCodeBlock(block); } private void ConditionalBlock(bool topLevel) { Assert(CSharpSymbolType.Keyword); Block block = new Block(CurrentSymbol); ConditionalBlock(block); if (topLevel) { CompleteBlock(); } } private void ConditionalBlock(Block block) { AcceptAndMoveNext(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); // Parse the condition, if present (if not present, we'll let the C# compiler complain) if (AcceptCondition()) { AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); ExpectCodeBlock(block); } } private bool AcceptCondition() { if (At(CSharpSymbolType.LeftParenthesis)) { bool complete = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates); if (!complete) { AcceptUntil(CSharpSymbolType.NewLine); } else { Optional(CSharpSymbolType.RightParenthesis); } return complete; } return true; } private void Statement() { Statement(null); } private void Statement(Block block) { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; // Accept whitespace but always keep the last whitespace node so we can put it back if necessary CSharpSymbol lastWs = AcceptWhiteSpaceInLines(); Debug.Assert(lastWs == null || (lastWs.Start.AbsoluteIndex + lastWs.Content.Length == CurrentLocation.AbsoluteIndex)); if (EndOfFile) { if (lastWs != null) { Accept(lastWs); } return; } CSharpSymbolType type = CurrentSymbol.Type; SourceLocation loc = CurrentLocation; bool isSingleLineMarkup = type == CSharpSymbolType.Transition && NextIs(CSharpSymbolType.Colon); bool isMarkup = isSingleLineMarkup || type == CSharpSymbolType.LessThan || (type == CSharpSymbolType.Transition && NextIs(CSharpSymbolType.LessThan)); if (Context.DesignTimeMode || !isMarkup) { // CODE owns whitespace, MARKUP owns it ONLY in DesignTimeMode. if (lastWs != null) { Accept(lastWs); } } else { // MARKUP owns whitespace EXCEPT in DesignTimeMode. PutCurrentBack(); PutBack(lastWs); } if (isMarkup) { if (type == CSharpSymbolType.Transition && !isSingleLineMarkup) { Context.OnError(loc, RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start); } // Markup block Output(SpanKind.Code); if (Context.DesignTimeMode && CurrentSymbol != null && (CurrentSymbol.Type == CSharpSymbolType.LessThan || CurrentSymbol.Type == CSharpSymbolType.Transition)) { PutCurrentBack(); } OtherParserBlock(); } else { // What kind of statement is this? HandleStatement(block, type); } } private void HandleStatement(Block block, CSharpSymbolType type) { switch (type) { case CSharpSymbolType.RazorCommentTransition: Output(SpanKind.Code); RazorComment(); Statement(block); break; case CSharpSymbolType.LeftBrace: // Verbatim Block block = block ?? new Block(RazorResources.BlockName_Code, CurrentLocation); AcceptAndMoveNext(); CodeBlock(block); break; case CSharpSymbolType.Keyword: // Keyword block HandleKeyword(false, StandardStatement); break; case CSharpSymbolType.Transition: // Embedded Expression block EmbeddedExpression(); break; case CSharpSymbolType.RightBrace: // Possible end of Code Block, just run the continuation break; case CSharpSymbolType.Comment: AcceptAndMoveNext(); break; default: // Other statement StandardStatement(); break; } } private void EmbeddedExpression() { // First, verify the type of the block Assert(CSharpSymbolType.Transition); CSharpSymbol transition = CurrentSymbol; NextToken(); if (At(CSharpSymbolType.Transition)) { // Escaped "@" Output(SpanKind.Code); // Output "@" as hidden span Accept(transition); Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.Code); Assert(CSharpSymbolType.Transition); AcceptAndMoveNext(); StandardStatement(); } else { // Throw errors as necessary, but continue parsing if (At(CSharpSymbolType.Keyword)) { Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_Keyword_After_At, CSharpLanguageCharacteristics.GetKeyword(CurrentSymbol.Keyword.Value)); } else if (At(CSharpSymbolType.LeftBrace)) { Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_Nested_CodeBlock); } // @( or @foo - Nested expression, parse a child block PutCurrentBack(); PutBack(transition); // Before exiting, add a marker span if necessary AddMarkerSymbolIfNecessary(); NestedBlock(); } } private void StandardStatement() { while (!EndOfFile) { int bookmark = CurrentLocation.AbsoluteIndex; IEnumerable read = ReadWhile(sym => sym.Type != CSharpSymbolType.Semicolon && sym.Type != CSharpSymbolType.RazorCommentTransition && sym.Type != CSharpSymbolType.Transition && sym.Type != CSharpSymbolType.LeftBrace && sym.Type != CSharpSymbolType.LeftParenthesis && sym.Type != CSharpSymbolType.LeftBracket && sym.Type != CSharpSymbolType.RightBrace); if (At(CSharpSymbolType.LeftBrace) || At(CSharpSymbolType.LeftParenthesis) || At(CSharpSymbolType.LeftBracket)) { Accept(read); if (Balance(BalancingModes.AllowCommentsAndTemplates | BalancingModes.BacktrackOnFailure)) { Optional(CSharpSymbolType.RightBrace); } else { // Recovery AcceptUntil(CSharpSymbolType.LessThan, CSharpSymbolType.RightBrace); return; } } else if (At(CSharpSymbolType.Transition) && (NextIs(CSharpSymbolType.LessThan, CSharpSymbolType.Colon))) { Accept(read); Output(SpanKind.Code); Template(); } else if (At(CSharpSymbolType.RazorCommentTransition)) { Accept(read); RazorComment(); } else if (At(CSharpSymbolType.Semicolon)) { Accept(read); AcceptAndMoveNext(); return; } else if (At(CSharpSymbolType.RightBrace)) { Accept(read); return; } else { Context.Source.Position = bookmark; NextToken(); AcceptUntil(CSharpSymbolType.LessThan, CSharpSymbolType.RightBrace); return; } } } private void CodeBlock(Block block) { CodeBlock(true, block); } private void CodeBlock(bool acceptTerminatingBrace, Block block) { EnsureCurrent(); while (!EndOfFile && !At(CSharpSymbolType.RightBrace)) { // Parse a statement, then return here Statement(); EnsureCurrent(); } if (EndOfFile) { Context.OnError(block.Start, RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, block.Name, '}', '{'); } else if (acceptTerminatingBrace) { Assert(CSharpSymbolType.RightBrace); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; AcceptAndMoveNext(); } } private void HandleKeyword(bool topLevel, Action fallback) { Debug.Assert(CurrentSymbol.Type == CSharpSymbolType.Keyword && CurrentSymbol.Keyword != null); Action handler; if (_keywordParsers.TryGetValue(CurrentSymbol.Keyword.Value, out handler)) { handler(topLevel); } else { fallback(); } } private IEnumerable SkipToNextImportantToken() { while (!EndOfFile) { IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); if (At(CSharpSymbolType.RazorCommentTransition)) { Accept(ws); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; RazorComment(); } else { return ws; } } return Enumerable.Empty(); } // Common code for Parsers, but FxCop REALLY doesn't like it in the base class.. moving it here for now. protected override void OutputSpanBeforeRazorComment() { AddMarkerSymbolIfNecessary(); Output(SpanKind.Code); } protected class Block { public Block(string name, SourceLocation start) { Name = name; Start = start; } public Block(CSharpSymbol symbol) : this(GetName(symbol), symbol.Start) { } public string Name { get; set; } public SourceLocation Start { get; set; } private static string GetName(CSharpSymbol sym) { if (sym.Type == CSharpSymbolType.Keyword) { return CSharpLanguageCharacteristics.GetKeyword(sym.Keyword.Value); } return sym.Content; } } } } ================================================ FILE: src/System.Web.Razor/Parser/CSharpCodeParser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Editor; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Tokenizer; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class CSharpCodeParser : TokenizerBackedParser { internal static readonly int UsingKeywordLength = 5; // using internal static ISet DefaultKeywords = new HashSet() { "if", "do", "try", "for", "foreach", "while", "switch", "lock", "using", "section", "inherits", "helper", "functions", "namespace", "class", "layout", "sessionstate" }; private Dictionary _directiveParsers = new Dictionary(); private Dictionary> _keywordParsers = new Dictionary>(); public CSharpCodeParser() { Keywords = new HashSet(); SetUpKeywords(); SetupDirectives(); } protected internal ISet Keywords { get; private set; } public bool IsNested { get; set; } protected override ParserBase OtherParser { get { return Context.MarkupParser; } } protected override LanguageCharacteristics Language { get { return CSharpLanguageCharacteristics.Instance; } } protected void MapDirectives(Action handler, params string[] directives) { foreach (string directive in directives) { _directiveParsers.Add(directive, handler); Keywords.Add(directive); } } protected bool TryGetDirectiveHandler(string directive, out Action handler) { return _directiveParsers.TryGetValue(directive, out handler); } private void MapKeywords(Action handler, params CSharpKeyword[] keywords) { MapKeywords(handler, topLevel: true, keywords: keywords); } private void MapKeywords(Action handler, bool topLevel, params CSharpKeyword[] keywords) { foreach (CSharpKeyword keyword in keywords) { _keywordParsers.Add(keyword, handler); if (topLevel) { Keywords.Add(CSharpLanguageCharacteristics.GetKeyword(keyword)); } } } [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This only occurs in Release builds, where this method is empty by design")] [Conditional("DEBUG")] internal void Assert(CSharpKeyword expectedKeyword) { Debug.Assert(CurrentSymbol.Type == CSharpSymbolType.Keyword && CurrentSymbol.Keyword.HasValue && CurrentSymbol.Keyword.Value == expectedKeyword); } protected internal bool At(CSharpKeyword keyword) { return At(CSharpSymbolType.Keyword) && CurrentSymbol.Keyword.HasValue && CurrentSymbol.Keyword.Value == keyword; } protected internal bool AcceptIf(CSharpKeyword keyword) { if (At(keyword)) { AcceptAndMoveNext(); return true; } return false; } protected static Func IsSpacingToken(bool includeNewLines, bool includeComments) { return sym => sym.Type == CSharpSymbolType.WhiteSpace || (includeNewLines && sym.Type == CSharpSymbolType.NewLine) || (includeComments && sym.Type == CSharpSymbolType.Comment); } public override void ParseBlock() { using (PushSpanConfig(DefaultSpanConfig)) { if (Context == null) { throw new InvalidOperationException(RazorResources.Parser_Context_Not_Set); } // Unless changed, the block is a statement block using (Context.StartBlock(BlockType.Statement)) { NextToken(); AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); CSharpSymbol current = CurrentSymbol; if (At(CSharpSymbolType.StringLiteral) && CurrentSymbol.Content.Length > 0 && CurrentSymbol.Content[0] == SyntaxConstants.TransitionCharacter) { Tuple split = Language.SplitSymbol(CurrentSymbol, 1, CSharpSymbolType.Transition); current = split.Item1; Context.Source.Position = split.Item2.Start.AbsoluteIndex; NextToken(); } else if (At(CSharpSymbolType.Transition)) { NextToken(); } // Accept "@" if we see it, but if we don't, that's OK. We assume we were started for a good reason if (current.Type == CSharpSymbolType.Transition) { if (Span.Symbols.Count > 0) { Output(SpanKind.Code); } AtTransition(current); } else { // No "@" => Jump straight to AfterTransition AfterTransition(); } Output(SpanKind.Code); } } } private void DefaultSpanConfig(SpanBuilder span) { span.EditHandler = SpanEditHandler.CreateDefault(Language.TokenizeString); span.CodeGenerator = new StatementCodeGenerator(); } private void AtTransition(CSharpSymbol current) { Debug.Assert(current.Type == CSharpSymbolType.Transition); Accept(current); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; // Output the "@" span and continue here Output(SpanKind.Transition); AfterTransition(); } private void AfterTransition() { using (PushSpanConfig(DefaultSpanConfig)) { EnsureCurrent(); try { // What type of block is this? if (!EndOfFile) { if (CurrentSymbol.Type == CSharpSymbolType.LeftParenthesis) { Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); ExplicitExpression(); return; } else if (CurrentSymbol.Type == CSharpSymbolType.Identifier) { Action handler; if (TryGetDirectiveHandler(CurrentSymbol.Content, out handler)) { Span.CodeGenerator = SpanCodeGenerator.Null; handler(); return; } else { Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); ImplicitExpression(); return; } } else if (CurrentSymbol.Type == CSharpSymbolType.Keyword) { KeywordBlock(topLevel: true); return; } else if (CurrentSymbol.Type == CSharpSymbolType.LeftBrace) { VerbatimBlock(); return; } } // Invalid character Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); AddMarkerSymbolIfNecessary(); Span.CodeGenerator = new ExpressionCodeGenerator(); Span.EditHandler = new ImplicitExpressionEditHandler( Language.TokenizeString, DefaultKeywords, acceptTrailingDot: IsNested) { AcceptedCharacters = AcceptedCharacters.NonWhiteSpace }; if (At(CSharpSymbolType.WhiteSpace) || At(CSharpSymbolType.NewLine)) { Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS); } else if (EndOfFile) { Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock); } else { Context.OnError(CurrentLocation, RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, CurrentSymbol.Content); } } finally { // Always put current character back in the buffer for the next parser. PutCurrentBack(); } } } private void VerbatimBlock() { Assert(CSharpSymbolType.LeftBrace); Block block = new Block(RazorResources.BlockName_Code, CurrentLocation); AcceptAndMoveNext(); // Set up the "{" span and output Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.MetaCode); // Set up auto-complete and parse the code block AutoCompleteEditHandler editHandler = new AutoCompleteEditHandler(Language.TokenizeString); Span.EditHandler = editHandler; CodeBlock(false, block); Span.CodeGenerator = new StatementCodeGenerator(); AddMarkerSymbolIfNecessary(); if (!At(CSharpSymbolType.RightBrace)) { editHandler.AutoCompleteString = "}"; } Output(SpanKind.Code); if (Optional(CSharpSymbolType.RightBrace)) { // Set up the "}" span Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; } if (!At(CSharpSymbolType.WhiteSpace) && !At(CSharpSymbolType.NewLine)) { PutCurrentBack(); } CompleteBlock(insertMarkerIfNecessary: false); Output(SpanKind.MetaCode); } private void ImplicitExpression() { Context.CurrentBlock.Type = BlockType.Expression; Context.CurrentBlock.CodeGenerator = new ExpressionCodeGenerator(); using (PushSpanConfig(span => { span.EditHandler = new ImplicitExpressionEditHandler(Language.TokenizeString, Keywords, acceptTrailingDot: IsNested); span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace; span.CodeGenerator = new ExpressionCodeGenerator(); })) { do { if (AtIdentifier(allowKeywords: true)) { AcceptAndMoveNext(); } } while (MethodCallOrArrayIndex()); PutCurrentBack(); Output(SpanKind.Code); } } private bool MethodCallOrArrayIndex() { if (!EndOfFile) { if (CurrentSymbol.Type == CSharpSymbolType.LeftParenthesis || CurrentSymbol.Type == CSharpSymbolType.LeftBracket) { // If we end within "(", whitespace is fine Span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; CSharpSymbolType right; bool success; using (PushSpanConfig((span, prev) => { prev(span); span.EditHandler.AcceptedCharacters = AcceptedCharacters.Any; })) { right = Language.FlipBracket(CurrentSymbol.Type); success = Balance(BalancingModes.BacktrackOnFailure | BalancingModes.AllowCommentsAndTemplates); } if (!success) { AcceptUntil(CSharpSymbolType.LessThan); } if (At(right)) { AcceptAndMoveNext(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.NonWhiteSpace; } return MethodCallOrArrayIndex(); } if (CurrentSymbol.Type == CSharpSymbolType.Dot) { CSharpSymbol dot = CurrentSymbol; if (NextToken()) { if (At(CSharpSymbolType.Identifier) || At(CSharpSymbolType.Keyword)) { // Accept the dot and return to the start Accept(dot); return true; // continue } else { // Put the symbol back PutCurrentBack(); } } if (!IsNested) { // Put the "." back PutBack(dot); } else { Accept(dot); } } else if (!At(CSharpSymbolType.WhiteSpace) && !At(CSharpSymbolType.NewLine)) { PutCurrentBack(); } } // Implicit Expression is complete return false; } private void CompleteBlock() { CompleteBlock(insertMarkerIfNecessary: true); } private void CompleteBlock(bool insertMarkerIfNecessary) { CompleteBlock(insertMarkerIfNecessary, captureWhitespaceToEndOfLine: insertMarkerIfNecessary); } private void CompleteBlock(bool insertMarkerIfNecessary, bool captureWhitespaceToEndOfLine) { if (insertMarkerIfNecessary && Context.LastAcceptedCharacters != AcceptedCharacters.Any) { AddMarkerSymbolIfNecessary(); } EnsureCurrent(); // Read whitespace, but not newlines // If we're not inserting a marker span, we don't need to capture whitespace if (!Context.WhiteSpaceIsSignificantToAncestorBlock && Context.CurrentBlock.Type != BlockType.Expression && captureWhitespaceToEndOfLine && !Context.DesignTimeMode && !IsNested) { CaptureWhitespaceAtEndOfCodeOnlyLine(); } else { PutCurrentBack(); } } private void CaptureWhitespaceAtEndOfCodeOnlyLine() { IEnumerable ws = ReadWhile(sym => sym.Type == CSharpSymbolType.WhiteSpace); if (At(CSharpSymbolType.NewLine)) { Accept(ws); AcceptAndMoveNext(); PutCurrentBack(); } else { PutCurrentBack(); PutBack(ws); } } private void ConfigureExplicitExpressionSpan(SpanBuilder sb) { sb.EditHandler = SpanEditHandler.CreateDefault(Language.TokenizeString); sb.CodeGenerator = new ExpressionCodeGenerator(); } private void ExplicitExpression() { Block block = new Block(RazorResources.BlockName_ExplicitExpression, CurrentLocation); Assert(CSharpSymbolType.LeftParenthesis); AcceptAndMoveNext(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.MetaCode); using (PushSpanConfig(ConfigureExplicitExpressionSpan)) { bool success = Balance( BalancingModes.BacktrackOnFailure | BalancingModes.NoErrorOnFailure | BalancingModes.AllowCommentsAndTemplates, CSharpSymbolType.LeftParenthesis, CSharpSymbolType.RightParenthesis, block.Start); if (!success) { AcceptUntil(CSharpSymbolType.LessThan); Context.OnError(block.Start, RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, block.Name, ")", "("); } // If necessary, put an empty-content marker symbol here if (Span.Symbols.Count == 0) { Accept(new CSharpSymbol(CurrentLocation, String.Empty, CSharpSymbolType.Unknown)); } // Output the content span and then capture the ")" Output(SpanKind.Code); } Optional(CSharpSymbolType.RightParenthesis); if (!EndOfFile) { PutCurrentBack(); } Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; CompleteBlock(insertMarkerIfNecessary: false); Output(SpanKind.MetaCode); } private void Template() { if (Context.IsWithin(BlockType.Template)) { Context.OnError(CurrentLocation, RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested); } Output(SpanKind.Code); using (Context.StartBlock(BlockType.Template)) { Context.CurrentBlock.CodeGenerator = new TemplateBlockCodeGenerator(); PutCurrentBack(); OtherParserBlock(); } } private void OtherParserBlock() { ParseWithOtherParser(p => p.ParseBlock()); } private void SectionBlock(string left, string right, bool caseSensitive) { ParseWithOtherParser(p => p.ParseSection(Tuple.Create(left, right), caseSensitive)); } private void NestedBlock() { Output(SpanKind.Code); bool wasNested = IsNested; IsNested = true; using (PushSpanConfig()) { ParseBlock(); } Initialize(Span); IsNested = wasNested; NextToken(); } protected override bool IsAtEmbeddedTransition(bool allowTemplatesAndComments, bool allowTransitions) { // No embedded transitions in C#, so ignore that param return allowTemplatesAndComments && ((Language.IsTransition(CurrentSymbol) && NextIs(CSharpSymbolType.LessThan, CSharpSymbolType.Colon)) || Language.IsCommentStart(CurrentSymbol)); } protected override void HandleEmbeddedTransition() { if (Language.IsTransition(CurrentSymbol)) { PutCurrentBack(); Template(); } else if (Language.IsCommentStart(CurrentSymbol)) { RazorComment(); } } private void ParseWithOtherParser(Action parseAction) { using (PushSpanConfig()) { Context.SwitchActiveParser(); parseAction(Context.MarkupParser); Context.SwitchActiveParser(); } Initialize(Span); NextToken(); } } } ================================================ FILE: src/System.Web.Razor/Parser/CSharpLanguageCharacteristics.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public class CSharpLanguageCharacteristics : LanguageCharacteristics { private static readonly CSharpLanguageCharacteristics _instance = new CSharpLanguageCharacteristics(); private static Dictionary _symbolSamples = new Dictionary() { { CSharpSymbolType.Arrow, "->" }, { CSharpSymbolType.Minus, "-" }, { CSharpSymbolType.Decrement, "--" }, { CSharpSymbolType.MinusAssign, "-=" }, { CSharpSymbolType.NotEqual, "!=" }, { CSharpSymbolType.Not, "!" }, { CSharpSymbolType.Modulo, "%" }, { CSharpSymbolType.ModuloAssign, "%=" }, { CSharpSymbolType.AndAssign, "&=" }, { CSharpSymbolType.And, "&" }, { CSharpSymbolType.DoubleAnd, "&&" }, { CSharpSymbolType.LeftParenthesis, "(" }, { CSharpSymbolType.RightParenthesis, ")" }, { CSharpSymbolType.Star, "*" }, { CSharpSymbolType.MultiplyAssign, "*=" }, { CSharpSymbolType.Comma, "," }, { CSharpSymbolType.Dot, "." }, { CSharpSymbolType.Slash, "/" }, { CSharpSymbolType.DivideAssign, "/=" }, { CSharpSymbolType.DoubleColon, "::" }, { CSharpSymbolType.Colon, ":" }, { CSharpSymbolType.Semicolon, ";" }, { CSharpSymbolType.QuestionMark, "?" }, { CSharpSymbolType.NullCoalesce, "??" }, { CSharpSymbolType.RightBracket, "]" }, { CSharpSymbolType.LeftBracket, "[" }, { CSharpSymbolType.XorAssign, "^=" }, { CSharpSymbolType.Xor, "^" }, { CSharpSymbolType.LeftBrace, "{" }, { CSharpSymbolType.OrAssign, "|=" }, { CSharpSymbolType.DoubleOr, "||" }, { CSharpSymbolType.Or, "|" }, { CSharpSymbolType.RightBrace, "}" }, { CSharpSymbolType.Tilde, "~" }, { CSharpSymbolType.Plus, "+" }, { CSharpSymbolType.PlusAssign, "+=" }, { CSharpSymbolType.Increment, "++" }, { CSharpSymbolType.LessThan, "<" }, { CSharpSymbolType.LessThanEqual, "<=" }, { CSharpSymbolType.LeftShift, "<<" }, { CSharpSymbolType.LeftShiftAssign, "<<=" }, { CSharpSymbolType.Assign, "=" }, { CSharpSymbolType.Equals, "==" }, { CSharpSymbolType.GreaterThan, ">" }, { CSharpSymbolType.GreaterThanEqual, ">=" }, { CSharpSymbolType.RightShift, ">>" }, { CSharpSymbolType.RightShiftAssign, ">>>" }, { CSharpSymbolType.Hash, "#" }, { CSharpSymbolType.Transition, "@" }, }; private CSharpLanguageCharacteristics() { } public static CSharpLanguageCharacteristics Instance { get { return _instance; } } public override CSharpTokenizer CreateTokenizer(ITextDocument source) { return new CSharpTokenizer(source); } protected override CSharpSymbol CreateSymbol(SourceLocation location, string content, CSharpSymbolType type, IEnumerable errors) { return new CSharpSymbol(location, content, type, errors); } public override string GetSample(CSharpSymbolType type) { return GetSymbolSample(type); } public override CSharpSymbol CreateMarkerSymbol(SourceLocation location) { return new CSharpSymbol(location, String.Empty, CSharpSymbolType.Unknown); } public override CSharpSymbolType GetKnownSymbolType(KnownSymbolType type) { switch (type) { case KnownSymbolType.Identifier: return CSharpSymbolType.Identifier; case KnownSymbolType.Keyword: return CSharpSymbolType.Keyword; case KnownSymbolType.NewLine: return CSharpSymbolType.NewLine; case KnownSymbolType.WhiteSpace: return CSharpSymbolType.WhiteSpace; case KnownSymbolType.Transition: return CSharpSymbolType.Transition; case KnownSymbolType.CommentStart: return CSharpSymbolType.RazorCommentTransition; case KnownSymbolType.CommentStar: return CSharpSymbolType.RazorCommentStar; case KnownSymbolType.CommentBody: return CSharpSymbolType.RazorComment; default: return CSharpSymbolType.Unknown; } } public override CSharpSymbolType FlipBracket(CSharpSymbolType bracket) { switch (bracket) { case CSharpSymbolType.LeftBrace: return CSharpSymbolType.RightBrace; case CSharpSymbolType.LeftBracket: return CSharpSymbolType.RightBracket; case CSharpSymbolType.LeftParenthesis: return CSharpSymbolType.RightParenthesis; case CSharpSymbolType.LessThan: return CSharpSymbolType.GreaterThan; case CSharpSymbolType.RightBrace: return CSharpSymbolType.LeftBrace; case CSharpSymbolType.RightBracket: return CSharpSymbolType.LeftBracket; case CSharpSymbolType.RightParenthesis: return CSharpSymbolType.LeftParenthesis; case CSharpSymbolType.GreaterThan: return CSharpSymbolType.LessThan; default: Debug.Fail("FlipBracket must be called with a bracket character"); return CSharpSymbolType.Unknown; } } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "C# Keywords are lower-case")] public static string GetKeyword(CSharpKeyword keyword) { return keyword.ToString().ToLowerInvariant(); } public static string GetSymbolSample(CSharpSymbolType type) { string sample; if (!_symbolSamples.TryGetValue(type, out sample)) { switch (type) { case CSharpSymbolType.Identifier: return RazorResources.CSharpSymbol_Identifier; case CSharpSymbolType.Keyword: return RazorResources.CSharpSymbol_Keyword; case CSharpSymbolType.IntegerLiteral: return RazorResources.CSharpSymbol_IntegerLiteral; case CSharpSymbolType.NewLine: return RazorResources.CSharpSymbol_Newline; case CSharpSymbolType.WhiteSpace: return RazorResources.CSharpSymbol_Whitespace; case CSharpSymbolType.Comment: return RazorResources.CSharpSymbol_Comment; case CSharpSymbolType.RealLiteral: return RazorResources.CSharpSymbol_RealLiteral; case CSharpSymbolType.CharacterLiteral: return RazorResources.CSharpSymbol_CharacterLiteral; case CSharpSymbolType.StringLiteral: return RazorResources.CSharpSymbol_StringLiteral; default: return RazorResources.Symbol_Unknown; } } return sample; } } } ================================================ FILE: src/System.Web.Razor/Parser/CallbackVisitor.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.Razor.Parser { public class CallbackVisitor : ParserVisitor { private Action _spanCallback; private Action _errorCallback; private Action _endBlockCallback; private Action _startBlockCallback; private Action _completeCallback; public CallbackVisitor(Action spanCallback) : this(spanCallback, _ => { }) { } public CallbackVisitor(Action spanCallback, Action errorCallback) : this(spanCallback, errorCallback, _ => { }, _ => { }) { } public CallbackVisitor(Action spanCallback, Action errorCallback, Action startBlockCallback, Action endBlockCallback) : this(spanCallback, errorCallback, startBlockCallback, endBlockCallback, () => { }) { } public CallbackVisitor(Action spanCallback, Action errorCallback, Action startBlockCallback, Action endBlockCallback, Action completeCallback) { _spanCallback = spanCallback; _errorCallback = errorCallback; _startBlockCallback = startBlockCallback; _endBlockCallback = endBlockCallback; _completeCallback = completeCallback; } public SynchronizationContext SynchronizationContext { get; set; } public override void VisitStartBlock(Block block) { base.VisitStartBlock(block); RaiseCallback(SynchronizationContext, block.Type, _startBlockCallback); } public override void VisitSpan(Span span) { base.VisitSpan(span); RaiseCallback(SynchronizationContext, span, _spanCallback); } public override void VisitEndBlock(Block block) { base.VisitEndBlock(block); RaiseCallback(SynchronizationContext, block.Type, _endBlockCallback); } public override void VisitError(RazorError err) { base.VisitError(err); RaiseCallback(SynchronizationContext, err, _errorCallback); } public override void OnComplete() { base.OnComplete(); RaiseCallback(SynchronizationContext, null, _ => _completeCallback()); } private static void RaiseCallback(SynchronizationContext syncContext, T param, Action callback) { if (callback != null) { if (syncContext != null) { syncContext.Post(state => callback((T)state), param); } else { callback(param); } } } } } ================================================ FILE: src/System.Web.Razor/Parser/ConditionalAttributeCollapser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; using System.Linq; using System.Web.Razor.Editor; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer; namespace System.Web.Razor.Parser { internal class ConditionalAttributeCollapser : MarkupRewriter { public ConditionalAttributeCollapser(Action markupSpanFactory) : base(markupSpanFactory) { } protected override bool CanRewrite(Block block) { AttributeBlockCodeGenerator gen = block.CodeGenerator as AttributeBlockCodeGenerator; return gen != null && block.Children.Any() && block.Children.All(IsLiteralAttributeValue); } protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block) { // Collect the content of this node string content = String.Concat(block.Children.Cast().Select(s => s.Content)); // Create a new span containing this content SpanBuilder span = new SpanBuilder(); span.EditHandler = new SpanEditHandler(HtmlTokenizer.Tokenize); FillSpan(span, block.Children.Cast().First().Start, content); return span.Build(); } private bool IsLiteralAttributeValue(SyntaxTreeNode node) { if (node.IsBlock) { return false; } Span span = node as Span; Debug.Assert(span != null); LiteralAttributeCodeGenerator litGen = span.CodeGenerator as LiteralAttributeCodeGenerator; return span != null && ((litGen != null && litGen.ValueGenerator == null) || span.CodeGenerator == SpanCodeGenerator.Null || span.CodeGenerator is MarkupCodeGenerator); } } } ================================================ FILE: src/System.Web.Razor/Parser/HtmlLanguageCharacteristics.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public class HtmlLanguageCharacteristics : LanguageCharacteristics { private static readonly HtmlLanguageCharacteristics _instance = new HtmlLanguageCharacteristics(); private HtmlLanguageCharacteristics() { } public static HtmlLanguageCharacteristics Instance { get { return _instance; } } public override string GetSample(HtmlSymbolType type) { switch (type) { case HtmlSymbolType.Text: return RazorResources.HtmlSymbol_Text; case HtmlSymbolType.WhiteSpace: return RazorResources.HtmlSymbol_WhiteSpace; case HtmlSymbolType.NewLine: return RazorResources.HtmlSymbol_NewLine; case HtmlSymbolType.OpenAngle: return "<"; case HtmlSymbolType.Bang: return "!"; case HtmlSymbolType.Solidus: return "/"; case HtmlSymbolType.QuestionMark: return "?"; case HtmlSymbolType.DoubleHyphen: return "--"; case HtmlSymbolType.LeftBracket: return "["; case HtmlSymbolType.CloseAngle: return ">"; case HtmlSymbolType.RightBracket: return "]"; case HtmlSymbolType.Equals: return "="; case HtmlSymbolType.DoubleQuote: return "\""; case HtmlSymbolType.SingleQuote: return "'"; case HtmlSymbolType.Transition: return "@"; case HtmlSymbolType.Colon: return ":"; case HtmlSymbolType.RazorComment: return RazorResources.HtmlSymbol_RazorComment; case HtmlSymbolType.RazorCommentStar: return "*"; case HtmlSymbolType.RazorCommentTransition: return "@"; default: return RazorResources.Symbol_Unknown; } } public override HtmlTokenizer CreateTokenizer(ITextDocument source) { return new HtmlTokenizer(source); } public override HtmlSymbolType FlipBracket(HtmlSymbolType bracket) { switch (bracket) { case HtmlSymbolType.LeftBracket: return HtmlSymbolType.RightBracket; case HtmlSymbolType.OpenAngle: return HtmlSymbolType.CloseAngle; case HtmlSymbolType.RightBracket: return HtmlSymbolType.LeftBracket; case HtmlSymbolType.CloseAngle: return HtmlSymbolType.OpenAngle; default: Debug.Fail("FlipBracket must be called with a bracket character"); return HtmlSymbolType.Unknown; } } public override HtmlSymbol CreateMarkerSymbol(SourceLocation location) { return new HtmlSymbol(location, String.Empty, HtmlSymbolType.Unknown); } public override HtmlSymbolType GetKnownSymbolType(KnownSymbolType type) { switch (type) { case KnownSymbolType.CommentStart: return HtmlSymbolType.RazorCommentTransition; case KnownSymbolType.CommentStar: return HtmlSymbolType.RazorCommentStar; case KnownSymbolType.CommentBody: return HtmlSymbolType.RazorComment; case KnownSymbolType.Identifier: return HtmlSymbolType.Text; case KnownSymbolType.Keyword: return HtmlSymbolType.Text; case KnownSymbolType.NewLine: return HtmlSymbolType.NewLine; case KnownSymbolType.Transition: return HtmlSymbolType.Transition; case KnownSymbolType.WhiteSpace: return HtmlSymbolType.WhiteSpace; default: return HtmlSymbolType.Unknown; } } protected override HtmlSymbol CreateSymbol(SourceLocation location, string content, HtmlSymbolType type, IEnumerable errors) { return new HtmlSymbol(location, content, type, errors); } } } ================================================ FILE: src/System.Web.Razor/Parser/HtmlMarkupParser.Block.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Web.Razor.Editor; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class HtmlMarkupParser { private SourceLocation _lastTagStart = SourceLocation.Zero; private HtmlSymbol _bufferedOpenAngle; public override void ParseBlock() { if (Context == null) { throw new InvalidOperationException(RazorResources.Parser_Context_Not_Set); } using (PushSpanConfig(DefaultMarkupSpan)) { using (Context.StartBlock(BlockType.Markup)) { if (!NextToken()) { return; } AcceptWhile(IsSpacingToken(includeNewLines: true)); if (CurrentSymbol.Type == HtmlSymbolType.OpenAngle) { // "<" => Implicit Tag Block TagBlock(new Stack>()); } else if (CurrentSymbol.Type == HtmlSymbolType.Transition) { // "@" => Explicit Tag/Single Line Block OR Template Output(SpanKind.Markup); // Definitely have a transition span Assert(HtmlSymbolType.Transition); AcceptAndMoveNext(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.Transition); if (At(HtmlSymbolType.Transition)) { Span.CodeGenerator = SpanCodeGenerator.Null; AcceptAndMoveNext(); Output(SpanKind.MetaCode); } AfterTransition(); } else { Context.OnError(CurrentSymbol.Start, RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag); } Output(SpanKind.Markup); } } } private void DefaultMarkupSpan(SpanBuilder span) { span.CodeGenerator = new MarkupCodeGenerator(); span.EditHandler = new SpanEditHandler(Language.TokenizeString, AcceptedCharacters.Any); } private void AfterTransition() { // "@:" => Explicit Single Line Block if (CurrentSymbol.Type == HtmlSymbolType.Text && CurrentSymbol.Content.Length > 0 && CurrentSymbol.Content[0] == ':') { // Split the token Tuple split = Language.SplitSymbol(CurrentSymbol, 1, HtmlSymbolType.Colon); // The first part (left) is added to this span and we return a MetaCode span Accept(split.Item1); Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.MetaCode); if (split.Item2 != null) { Accept(split.Item2); } NextToken(); SingleLineMarkup(); } else if (CurrentSymbol.Type == HtmlSymbolType.OpenAngle) { TagBlock(new Stack>()); } } private void SingleLineMarkup() { // Parse until a newline, it's that simple! // First, signal to code parser that whitespace is significant to us. bool old = Context.WhiteSpaceIsSignificantToAncestorBlock; Context.WhiteSpaceIsSignificantToAncestorBlock = true; Span.EditHandler = new SingleLineMarkupEditHandler(Language.TokenizeString); SkipToAndParseCode(HtmlSymbolType.NewLine); if (!EndOfFile && CurrentSymbol.Type == HtmlSymbolType.NewLine) { AcceptAndMoveNext(); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } PutCurrentBack(); Context.WhiteSpaceIsSignificantToAncestorBlock = old; Output(SpanKind.Markup); } private void TagBlock(Stack> tags) { // Skip Whitespace and Text bool complete = false; do { SkipToAndParseCode(HtmlSymbolType.OpenAngle); if (EndOfFile) { EndTagBlock(tags, complete: true); } else { _bufferedOpenAngle = null; _lastTagStart = CurrentLocation; Assert(HtmlSymbolType.OpenAngle); _bufferedOpenAngle = CurrentSymbol; SourceLocation tagStart = CurrentLocation; if (!NextToken()) { Accept(_bufferedOpenAngle); EndTagBlock(tags, complete: false); } else { complete = AfterTagStart(tagStart, tags); } } } while (tags.Count > 0); EndTagBlock(tags, complete); } private bool AfterTagStart(SourceLocation tagStart, Stack> tags) { if (!EndOfFile) { switch (CurrentSymbol.Type) { case HtmlSymbolType.Solidus: // End Tag return EndTag(tagStart, tags); case HtmlSymbolType.Bang: // Comment Accept(_bufferedOpenAngle); return BangTag(); case HtmlSymbolType.QuestionMark: // XML PI Accept(_bufferedOpenAngle); return XmlPI(); default: // Start Tag return StartTag(tags); } } if (tags.Count == 0) { Context.OnError(CurrentLocation, RazorResources.ParseError_OuterTagMissingName); } return false; } private bool XmlPI() { // Accept "?" Assert(HtmlSymbolType.QuestionMark); AcceptAndMoveNext(); return AcceptUntilAll(HtmlSymbolType.QuestionMark, HtmlSymbolType.CloseAngle); } private bool BangTag() { // Accept "!" Assert(HtmlSymbolType.Bang); if (AcceptAndMoveNext()) { if (CurrentSymbol.Type == HtmlSymbolType.DoubleHyphen) { AcceptAndMoveNext(); return AcceptUntilAll(HtmlSymbolType.DoubleHyphen, HtmlSymbolType.CloseAngle); } else if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket) { if (AcceptAndMoveNext()) { return CData(); } } else { AcceptAndMoveNext(); return AcceptUntilAll(HtmlSymbolType.CloseAngle); } } return false; } private bool CData() { if (CurrentSymbol.Type == HtmlSymbolType.Text && String.Equals(CurrentSymbol.Content, "cdata", StringComparison.OrdinalIgnoreCase)) { if (AcceptAndMoveNext()) { if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket) { return AcceptUntilAll(HtmlSymbolType.RightBracket, HtmlSymbolType.RightBracket, HtmlSymbolType.CloseAngle); } } } return false; } private bool EndTag(SourceLocation tagStart, Stack> tags) { // Accept "/" and move next Assert(HtmlSymbolType.Solidus); HtmlSymbol solidus = CurrentSymbol; if (!NextToken()) { Accept(_bufferedOpenAngle); Accept(solidus); return false; } else { string tagName = String.Empty; if (At(HtmlSymbolType.Text)) { tagName = CurrentSymbol.Content; } bool matched = RemoveTag(tags, tagName, tagStart); if (tags.Count == 0 && String.Equals(tagName, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase) && matched) { Output(SpanKind.Markup); return EndTextTag(solidus); } Accept(_bufferedOpenAngle); Accept(solidus); AcceptUntil(HtmlSymbolType.CloseAngle); // Accept the ">" return Optional(HtmlSymbolType.CloseAngle); } } private bool EndTextTag(HtmlSymbol solidus) { SourceLocation start = _bufferedOpenAngle.Start; Accept(_bufferedOpenAngle); Accept(solidus); Assert(HtmlSymbolType.Text); AcceptAndMoveNext(); bool seenCloseAngle = Optional(HtmlSymbolType.CloseAngle); if (!seenCloseAngle) { Context.OnError(start, RazorResources.ParseError_TextTagCannotContainAttributes); } else { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.Transition); return seenCloseAngle; } private bool IsTagRecoveryStopPoint(HtmlSymbol sym) { return sym.Type == HtmlSymbolType.CloseAngle || sym.Type == HtmlSymbolType.Solidus || sym.Type == HtmlSymbolType.OpenAngle || sym.Type == HtmlSymbolType.SingleQuote || sym.Type == HtmlSymbolType.DoubleQuote; } private void TagContent() { if (!At(HtmlSymbolType.WhiteSpace)) { // We should be right after the tag name, so if there's no whitespace, something is wrong RecoverToEndOfTag(); } else { // We are here ($): while (!EndOfFile && !IsEndOfTag()) { BeforeAttribute(); } } } private bool IsEndOfTag() { if (At(HtmlSymbolType.Solidus)) { if (NextIs(HtmlSymbolType.CloseAngle)) { return true; } else { AcceptAndMoveNext(); } } return At(HtmlSymbolType.CloseAngle) || At(HtmlSymbolType.OpenAngle); } private void BeforeAttribute() { // http://dev.w3.org/html5/spec/tokenization.html#before-attribute-name-state // Capture whitespace var whitespace = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine); if (At(HtmlSymbolType.Transition)) { // Transition outside of attribute value => Switch to recovery mode Accept(whitespace); RecoverToEndOfTag(); return; } // http://dev.w3.org/html5/spec/tokenization.html#attribute-name-state // Read the 'name' (i.e. read until the '=' or whitespace/newline) var name = Enumerable.Empty(); if (At(HtmlSymbolType.Text)) { name = ReadWhile(sym => sym.Type != HtmlSymbolType.WhiteSpace && sym.Type != HtmlSymbolType.NewLine && sym.Type != HtmlSymbolType.Equals && sym.Type != HtmlSymbolType.CloseAngle && sym.Type != HtmlSymbolType.OpenAngle && (sym.Type != HtmlSymbolType.Solidus || !NextIs(HtmlSymbolType.CloseAngle))); } else { // Unexpected character in tag, enter recovery Accept(whitespace); RecoverToEndOfTag(); return; } if (!At(HtmlSymbolType.Equals)) { // Saw a space or newline after the name, so just skip this attribute and continue around the loop Accept(whitespace); Accept(name); return; } Output(SpanKind.Markup); // Start a new markup block for the attribute using (Context.StartBlock(BlockType.Markup)) { AttributePrefix(whitespace, name); } } private void AttributePrefix(IEnumerable whitespace, IEnumerable nameSymbols) { // First, determine if this is a 'data-' attribute (since those can't use conditional attributes) LocationTagged name = nameSymbols.GetContent(Span.Start); bool attributeCanBeConditional = !name.Value.StartsWith("data-", StringComparison.OrdinalIgnoreCase); // Accept the whitespace and name Accept(whitespace); Accept(nameSymbols); Assert(HtmlSymbolType.Equals); // We should be at "=" AcceptAndMoveNext(); HtmlSymbolType quote = HtmlSymbolType.Unknown; if (At(HtmlSymbolType.SingleQuote) || At(HtmlSymbolType.DoubleQuote)) { quote = CurrentSymbol.Type; AcceptAndMoveNext(); } // We now have the prefix: (i.e. ' foo="') LocationTagged prefix = Span.GetContent(); if (attributeCanBeConditional) { Span.CodeGenerator = SpanCodeGenerator.Null; // The block code generator will render the prefix Output(SpanKind.Markup); // Read the values while (!EndOfFile && !IsEndOfAttributeValue(quote, CurrentSymbol)) { AttributeValue(quote); } // Capture the suffix LocationTagged suffix = new LocationTagged(String.Empty, CurrentLocation); if (quote != HtmlSymbolType.Unknown && At(quote)) { suffix = CurrentSymbol.GetContent(); AcceptAndMoveNext(); } if (Span.Symbols.Count > 0) { Span.CodeGenerator = SpanCodeGenerator.Null; // Again, block code generator will render the suffix Output(SpanKind.Markup); } // Create the block code generator Context.CurrentBlock.CodeGenerator = new AttributeBlockCodeGenerator( name, prefix, suffix); } else { // Not a "conditional" attribute, so just read the value SkipToAndParseCode(sym => IsEndOfAttributeValue(quote, sym)); if (quote != HtmlSymbolType.Unknown) { Optional(quote); } Output(SpanKind.Markup); } } private void AttributeValue(HtmlSymbolType quote) { SourceLocation prefixStart = CurrentLocation; var prefix = ReadWhile(sym => sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine); Accept(prefix); if (At(HtmlSymbolType.Transition)) { SourceLocation valueStart = CurrentLocation; PutCurrentBack(); // Output the prefix but as a null-span. DynamicAttributeBlockCodeGenerator will render it Span.CodeGenerator = SpanCodeGenerator.Null; // Dynamic value, start a new block and set the code generator using (Context.StartBlock(BlockType.Markup)) { Context.CurrentBlock.CodeGenerator = new DynamicAttributeBlockCodeGenerator(prefix.GetContent(prefixStart), valueStart); OtherParserBlock(); } } else if (At(HtmlSymbolType.Text) && CurrentSymbol.Content.Length > 0 && CurrentSymbol.Content[0] == '~' && NextIs(HtmlSymbolType.Solidus)) { // Virtual Path value SourceLocation valueStart = CurrentLocation; VirtualPath(); Span.CodeGenerator = new LiteralAttributeCodeGenerator( prefix.GetContent(prefixStart), new LocationTagged(new ResolveUrlCodeGenerator(), valueStart)); } else { // Literal value // 'quote' should be "Unknown" if not quoted and symbols coming from the tokenizer should never have "Unknown" type. var value = ReadWhile(sym => // These three conditions find separators which break the attribute value into portions sym.Type != HtmlSymbolType.WhiteSpace && sym.Type != HtmlSymbolType.NewLine && sym.Type != HtmlSymbolType.Transition && // This condition checks for the end of the attribute value (it repeats some of the checks above but for now that's ok) !IsEndOfAttributeValue(quote, sym)); Accept(value); Span.CodeGenerator = new LiteralAttributeCodeGenerator(prefix.GetContent(prefixStart), value.GetContent(prefixStart)); } Output(SpanKind.Markup); } private bool IsEndOfAttributeValue(HtmlSymbolType quote, HtmlSymbol sym) { return EndOfFile || sym == null || (quote != HtmlSymbolType.Unknown ? sym.Type == quote // If quoted, just wait for the quote : IsUnquotedEndOfAttributeValue(sym)); } private bool IsUnquotedEndOfAttributeValue(HtmlSymbol sym) { // If unquoted, we have a larger set of terminating characters: // http://dev.w3.org/html5/spec/tokenization.html#attribute-value-unquoted-state // Also we need to detect "/" and ">" return sym.Type == HtmlSymbolType.DoubleQuote || sym.Type == HtmlSymbolType.SingleQuote || sym.Type == HtmlSymbolType.OpenAngle || sym.Type == HtmlSymbolType.Equals || (sym.Type == HtmlSymbolType.Solidus && NextIs(HtmlSymbolType.CloseAngle)) || sym.Type == HtmlSymbolType.CloseAngle || sym.Type == HtmlSymbolType.WhiteSpace || sym.Type == HtmlSymbolType.NewLine; } private void VirtualPath() { Assert(HtmlSymbolType.Text); Debug.Assert(CurrentSymbol.Content.Length > 0 && CurrentSymbol.Content[0] == '~'); // Parse until a transition symbol, whitespace, newline or quote. We support only a fairly minimal subset of Virtual Paths AcceptUntil(HtmlSymbolType.Transition, HtmlSymbolType.WhiteSpace, HtmlSymbolType.NewLine, HtmlSymbolType.SingleQuote, HtmlSymbolType.DoubleQuote); // Output a Virtual Path span Span.EditHandler.EditorHints = EditorHints.VirtualPath; } private void RecoverToEndOfTag() { // Accept until ">", "/" or "<", but parse code while (!EndOfFile) { SkipToAndParseCode(IsTagRecoveryStopPoint); if (!EndOfFile) { EnsureCurrent(); switch (CurrentSymbol.Type) { case HtmlSymbolType.SingleQuote: case HtmlSymbolType.DoubleQuote: ParseQuoted(); break; case HtmlSymbolType.OpenAngle: // Another "<" means this tag is invalid. case HtmlSymbolType.Solidus: // Empty tag case HtmlSymbolType.CloseAngle: // End of tag return; default: AcceptAndMoveNext(); break; } } } } private void ParseQuoted() { HtmlSymbolType type = CurrentSymbol.Type; AcceptAndMoveNext(); ParseQuoted(type); } private void ParseQuoted(HtmlSymbolType type) { SkipToAndParseCode(type); if (!EndOfFile) { Assert(type); AcceptAndMoveNext(); } } private bool StartTag(Stack> tags) { // If we're at text, it's the name, otherwise the name is "" HtmlSymbol tagName; if (At(HtmlSymbolType.Text)) { tagName = CurrentSymbol; } else { tagName = new HtmlSymbol(CurrentLocation, String.Empty, HtmlSymbolType.Unknown); } Tuple tag = Tuple.Create(tagName, _lastTagStart); if (tags.Count == 0 && String.Equals(tag.Item1.Content, SyntaxConstants.TextTagName, StringComparison.OrdinalIgnoreCase)) { Output(SpanKind.Markup); Span.CodeGenerator = SpanCodeGenerator.Null; Accept(_bufferedOpenAngle); Assert(HtmlSymbolType.Text); AcceptAndMoveNext(); int bookmark = CurrentLocation.AbsoluteIndex; IEnumerable tokens = ReadWhile(IsSpacingToken(includeNewLines: true)); bool empty = At(HtmlSymbolType.Solidus); if (empty) { Accept(tokens); Assert(HtmlSymbolType.Solidus); AcceptAndMoveNext(); bookmark = CurrentLocation.AbsoluteIndex; tokens = ReadWhile(IsSpacingToken(includeNewLines: true)); } if (!Optional(HtmlSymbolType.CloseAngle)) { Context.Source.Position = bookmark; NextToken(); Context.OnError(tag.Item2, RazorResources.ParseError_TextTagCannotContainAttributes); } else { Accept(tokens); Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } if (!empty) { tags.Push(tag); } Output(SpanKind.Transition); return true; } Accept(_bufferedOpenAngle); Optional(HtmlSymbolType.Text); return RestOfTag(tag, tags); } private bool RestOfTag(Tuple tag, Stack> tags) { TagContent(); // We are now at a possible end of the tag // Found '<', so we just abort this tag. if (At(HtmlSymbolType.OpenAngle)) { return false; } bool isEmpty = At(HtmlSymbolType.Solidus); // Found a solidus, so don't accept it but DON'T push the tag to the stack if (isEmpty) { AcceptAndMoveNext(); } // Check for the '>' to determine if the tag is finished bool seenClose = Optional(HtmlSymbolType.CloseAngle); if (!seenClose) { Context.OnError(tag.Item2, RazorResources.ParseError_UnfinishedTag, tag.Item1.Content); } else { if (!isEmpty) { // Is this a void element? string tagName = tag.Item1.Content.Trim(); if (VoidElements.Contains(tagName)) { // Technically, void elements like "meta" are not allowed to have end tags. Just in case they do, // we need to look ahead at the next set of tokens. If we see "<", "/", tag name, accept it and the ">" following it // Place a bookmark int bookmark = CurrentLocation.AbsoluteIndex; // Skip whitespace IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true)); // Open Angle if (At(HtmlSymbolType.OpenAngle) && NextIs(HtmlSymbolType.Solidus)) { HtmlSymbol openAngle = CurrentSymbol; NextToken(); Assert(HtmlSymbolType.Solidus); HtmlSymbol solidus = CurrentSymbol; NextToken(); if (At(HtmlSymbolType.Text) && String.Equals(CurrentSymbol.Content, tagName, StringComparison.OrdinalIgnoreCase)) { // Accept up to here Accept(ws); Accept(openAngle); Accept(solidus); AcceptAndMoveNext(); // Accept to '>', '<' or EOF AcceptUntil(HtmlSymbolType.CloseAngle, HtmlSymbolType.OpenAngle); // Accept the '>' if we saw it. And if we do see it, we're complete return Optional(HtmlSymbolType.CloseAngle); } // At(HtmlSymbolType.Text) && String.Equals(CurrentSymbol.Content, tagName, StringComparison.OrdinalIgnoreCase) } // At(HtmlSymbolType.OpenAngle) && NextIs(HtmlSymbolType.Solidus) // Go back to the bookmark and just finish this tag at the close angle Context.Source.Position = bookmark; NextToken(); } else if (String.Equals(tagName, "script", StringComparison.OrdinalIgnoreCase)) { SkipToEndScriptAndParseCode(); } else { // Push the tag on to the stack tags.Push(tag); } } } return seenClose; } private void SkipToEndScriptAndParseCode() { // Special case for } @Html.ValidationSummary()
@AntiForgery.GetHtml()
  1. @Html.Password("password") @Html.ValidationMessage("password", "*")

@AdminResources.ForgotPassword


@{ var passwordFileLocation = AdminSecurity.AdminPasswordFile.TrimStart('~', '/'); var forgotPasswordHelp = String.Format(CultureInfo.CurrentCulture, AdminResources.AdminPasswordChangeInstructions, Html.Encode(passwordFileLocation)); } ================================================ FILE: src/System.Web.WebPages.Administration/Logout.cshtml ================================================ @* Generator: WebPage *@ @{ // Delete the admin auth cookie AdminSecurity.DeleteAuthCookie(Response); // Redirect home SiteAdmin.RedirectToHome(Response); } ================================================ FILE: src/System.Web.WebPages.Administration/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using System.Web.WebPages.Administration; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("System.Web.WebPages.Administration")] [assembly: AssemblyDescription("")] [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] [assembly: InternalsVisibleTo("System.Web.WebPages.Administration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] ================================================ FILE: src/System.Web.WebPages.Administration/Register.cshtml ================================================ @* Generator: WebPage *@ @using System.Globalization; @{ Page.Title = AdminResources.RegisterTitle; var adminPath = SiteAdmin.AdminVirtualPath.TrimStart('~'); Page.Desc = String.Format(CultureInfo.CurrentCulture, AdminResources.RegisterDesc, Html.Encode(adminPath)); // If the password is already set the redirect to login if(AdminSecurity.HasAdminPassword()) { SiteAdmin.RedirectToLogin(Response); return; } if (IsPost) { AntiForgery.Validate(); var password = Request.Form["password"]; var reenteredPassword = Request.Form["repassword"]; if (password.IsEmpty()) { ModelState.AddError("password", AdminResources.Validation_PasswordRequired); } else if (password != reenteredPassword) { ModelState.AddError("repassword", AdminResources.Validation_PasswordsDoNotMatch); } if (ModelState.IsValid) { // Save the admin password if(AdminSecurity.SaveTemporaryPassword(password)) { // Get the return url var returnUrl = SiteAdmin.GetReturnUrl(Request) ?? SiteAdmin.AdminVirtualPath; // Redirect to the return url Response.Redirect(returnUrl); } else { // Add a validation error since creating the password.txt failed ModelState.AddFormError(AdminResources.AdminModuleRequiresAccessToAppData); } } } }
@Html.ValidationSummary()
@AntiForgery.GetHtml()
  1. @Html.Password("password") @Html.ValidationMessage("password", "*")
  2. @Html.Password("repassword") @Html.ValidationMessage("repassword", "*")

================================================ FILE: src/System.Web.WebPages.Administration/Resources/AdminResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.214 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.WebPages.Administration { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class AdminResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AdminResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.WebPages.Administration.Resources.AdminResources", typeof(AdminResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Home. /// internal static string AdminHome { get { return ResourceManager.GetString("AdminHome", resourceCulture); } } /// /// Looks up a localized string similar to Web Pages Administration. /// internal static string AdminModuleDisplayTitle { get { return ResourceManager.GetString("AdminModuleDisplayTitle", resourceCulture); } } /// /// Looks up a localized string similar to The Admin Module requires write access to ~/App_Data. /// internal static string AdminModuleRequiresAccessToAppData { get { return ResourceManager.GetString("AdminModuleRequiresAccessToAppData", resourceCulture); } } /// /// Looks up a localized string similar to ASP.NET Web Pages Administration. /// internal static string AdminModuleTitle { get { return ResourceManager.GetString("AdminModuleTitle", resourceCulture); } } /// /// Looks up a localized string similar to To reset the password, delete the <strong>{0}</strong> file and revisit this page.. /// internal static string AdminPasswordChangeInstructions { get { return ResourceManager.GetString("AdminPasswordChangeInstructions", resourceCulture); } } /// /// Looks up a localized string similar to <a href="{0}">Click here</a> to continue and verify your password after you have renamed the file to Password.config.. /// internal static string ContinueAfterEnableText { get { return ResourceManager.GetString("ContinueAfterEnableText", resourceCulture); } } /// /// Looks up a localized string similar to Create Password. /// internal static string CreatePassword { get { return ResourceManager.GetString("CreatePassword", resourceCulture); } } /// /// Looks up a localized string similar to For security reasons your password hash is saved in a file named _Password.config in the /App_Data/Admin/ folder of your website. To fully enable site administration, rename the file to Password.config by removing the underscore (_) character from the file name. If this is the first time you are seeing these instructions and you have not yet created a password, then remove the /App_Data/Admin/_Password.config file. This will remove a previously created password and allow you to create your own password. /// internal static string EnableInstructions { get { return ResourceManager.GetString("EnableInstructions", resourceCulture); } } /// /// Looks up a localized string similar to Enter Password. /// internal static string EnterPassword { get { return ResourceManager.GetString("EnterPassword", resourceCulture); } } /// /// Looks up a localized string similar to Forgot Password?. /// internal static string ForgotPassword { get { return ResourceManager.GetString("ForgotPassword", resourceCulture); } } /// /// Looks up a localized string similar to The return URL specified for request redirection is invalid.. /// internal static string InvalidReturnUrl { get { return ResourceManager.GetString("InvalidReturnUrl", resourceCulture); } } /// /// Looks up a localized string similar to Login. /// internal static string Login { get { return ResourceManager.GetString("Login", resourceCulture); } } /// /// Looks up a localized string similar to Login to ASP.NET Web Pages Administration. /// internal static string LoginTitle { get { return ResourceManager.GetString("LoginTitle", resourceCulture); } } /// /// Looks up a localized string similar to Logo. /// internal static string LogoLabel { get { return ResourceManager.GetString("LogoLabel", resourceCulture); } } /// /// Looks up a localized string similar to Logout. /// internal static string Logout { get { return ResourceManager.GetString("Logout", resourceCulture); } } /// /// Looks up a localized string similar to Module already registered for virtual path '{0}'. /// internal static string ModuleAlreadyRegistered { get { return ResourceManager.GetString("ModuleAlreadyRegistered", resourceCulture); } } /// /// Looks up a localized string similar to Modules. /// internal static string Modules { get { return ResourceManager.GetString("Modules", resourceCulture); } } /// /// Looks up a localized string similar to No modules are currently installed.. /// internal static string NoAdminModulesInstalled { get { return ResourceManager.GetString("NoAdminModulesInstalled", resourceCulture); } } /// /// Looks up a localized string similar to Password. /// internal static string Password { get { return ResourceManager.GetString("Password", resourceCulture); } } /// /// Looks up a localized string similar to Confirm Password. /// internal static string ReenterPassword { get { return ResourceManager.GetString("ReenterPassword", resourceCulture); } } /// /// Looks up a localized string similar to Accessing pages in ASP.NET Web Pages Package Administration requires a password. You create the password the first time you visit a page in this directory.. /// internal static string RegisterDesc { get { return ResourceManager.GetString("RegisterDesc", resourceCulture); } } /// /// Looks up a localized string similar to Create a password. /// internal static string RegisterTitle { get { return ResourceManager.GetString("RegisterTitle", resourceCulture); } } /// /// Looks up a localized string similar to ASP.NET Web Pages Administration Security Check. /// internal static string SecurityTitle { get { return ResourceManager.GetString("SecurityTitle", resourceCulture); } } /// /// Looks up a localized string similar to The password in incorrect. /// internal static string Validation_PasswordIncorrect { get { return ResourceManager.GetString("Validation_PasswordIncorrect", resourceCulture); } } /// /// Looks up a localized string similar to A password is required.. /// internal static string Validation_PasswordRequired { get { return ResourceManager.GetString("Validation_PasswordRequired", resourceCulture); } } /// /// Looks up a localized string similar to Confirm password does not match password. /// internal static string Validation_PasswordsDoNotMatch { get { return ResourceManager.GetString("Validation_PasswordsDoNotMatch", resourceCulture); } } } } ================================================ FILE: src/System.Web.WebPages.Administration/Resources/AdminResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ASP.NET Web Pages Administration Login to ASP.NET Web Pages Administration Create a password Module already registered for virtual path '{0}' No modules are currently installed. The password in incorrect A password is required. Logout Login Password The Admin Module requires write access to ~/App_Data Create Password The return URL specified for request redirection is invalid. Enter Password Confirm Password Confirm password does not match password For security reasons your password hash is saved in a file named _Password.config in the /App_Data/Admin/ folder of your website. To fully enable site administration, rename the file to Password.config by removing the underscore (_) character from the file name. If this is the first time you are seeing these instructions and you have not yet created a password, then remove the /App_Data/Admin/_Password.config file. This will remove a previously created password and allow you to create your own password This resource string will be printed without html encoding. Ensure parameters are html encoded. To reset the password, delete the <strong>{0}</strong> file and revisit this page. This resource string will be printed without html encoding. Ensure parameters are html encoded. Forgot Password? Modules Accessing pages in ASP.NET Web Pages Package Administration requires a password. You create the password the first time you visit a page in this directory. Home Web Pages Administration Logo <a href="{0}">Click here</a> to continue and verify your password after you have renamed the file to Password.config. This resource string will be printed without html encoding. Ensure parameters are html encoded. ASP.NET Web Pages Administration Security Check ================================================ FILE: src/System.Web.WebPages.Administration/Resources/PackageManagerResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.488 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.WebPages.Administration.PackageManager { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class PackageManagerResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal PackageManagerResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.WebPages.Administration.Resources.PackageManagerResources", typeof(PackageManagerResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Add Package Source. /// internal static string AddPackageSourceLabel { get { return ResourceManager.GetString("AddPackageSourceLabel", resourceCulture); } } /// /// Looks up a localized string similar to Are you sure you want to uninstall <strong>{0}</strong>?. /// internal static string AreYouSureUninstall { get { return ResourceManager.GetString("AreYouSureUninstall", resourceCulture); } } /// /// Looks up a localized string similar to Author. /// internal static string AuthorsLabel { get { return ResourceManager.GetString("AuthorsLabel", resourceCulture); } } /// /// Looks up a localized string similar to Bad Request. /// internal static string BadRequest { get { return ResourceManager.GetString("BadRequest", resourceCulture); } } /// /// Looks up a localized string similar to Cancel. /// internal static string Cancel { get { return ResourceManager.GetString("Cancel", resourceCulture); } } /// /// Looks up a localized string similar to Clear. /// internal static string ClearLabel { get { return ResourceManager.GetString("ClearLabel", resourceCulture); } } /// /// Looks up a localized string similar to Default. /// internal static string DefaultPackageSourceName { get { return ResourceManager.GetString("DefaultPackageSourceName", resourceCulture); } } /// /// Looks up a localized string similar to Delete. /// internal static string DeleteLabel { get { return ResourceManager.GetString("DeleteLabel", resourceCulture); } } /// /// Looks up a localized string similar to By clicking "Install" you agree to the license terms for the package(s) above. If you do not agree to the license terms, click "Cancel." Each package is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.. /// internal static string Disclaimer { get { return ResourceManager.GetString("Disclaimer", resourceCulture); } } /// /// Looks up a localized string similar to Installed. /// internal static string InstalledLabel { get { return ResourceManager.GetString("InstalledLabel", resourceCulture); } } /// /// Looks up a localized string similar to Install. /// internal static string InstallPackage { get { return ResourceManager.GetString("InstallPackage", resourceCulture); } } /// /// Looks up a localized string similar to Installing package {0}.. /// internal static string InstallPackageDesc { get { return ResourceManager.GetString("InstallPackageDesc", resourceCulture); } } /// /// Looks up a localized string similar to Package <strong>{0}</strong> was successfully installed.. /// internal static string InstallSuccess { get { return ResourceManager.GetString("InstallSuccess", resourceCulture); } } /// /// Looks up a localized string similar to JavaScript is required to correctly view this page.. /// internal static string JavascriptRequired { get { return ResourceManager.GetString("JavascriptRequired", resourceCulture); } } /// /// Looks up a localized string similar to Use this page to manage your package sources.. /// internal static string ManageSourcesDesc { get { return ResourceManager.GetString("ManageSourcesDesc", resourceCulture); } } /// /// Looks up a localized string similar to Manage Package Sources. /// internal static string ManageSourcesTitle { get { return ResourceManager.GetString("ManageSourcesTitle", resourceCulture); } } /// /// Looks up a localized string similar to A collection of tools to automate the process of installing, upgrading, configuring, and removing packages from an ASP.NET application.. /// internal static string ModuleDesc { get { return ResourceManager.GetString("ModuleDesc", resourceCulture); } } /// /// Looks up a localized string similar to Package Manager. /// internal static string ModuleTitle { get { return ResourceManager.GetString("ModuleTitle", resourceCulture); } } /// /// Looks up a localized string similar to Next. /// internal static string NextText { get { return ResourceManager.GetString("NextText", resourceCulture); } } /// /// Looks up a localized string similar to No packages found.. /// internal static string NoPackagesFound { get { return ResourceManager.GetString("NoPackagesFound", resourceCulture); } } /// /// Looks up a localized string similar to No packages installed. <p><a href="{0}">Install packages from an online feed</a></p>. /// internal static string NoPackagesInstalled { get { return ResourceManager.GetString("NoPackagesInstalled", resourceCulture); } } /// /// Looks up a localized string similar to Default (All). /// internal static string NuGetFeed { get { return ResourceManager.GetString("NuGetFeed", resourceCulture); } } /// /// Looks up a localized string similar to Online. /// internal static string OnlineLabel { get { return ResourceManager.GetString("OnlineLabel", resourceCulture); } } /// /// Looks up a localized string similar to Error installing package "{0}":. /// internal static string PackageInstallationError { get { return ResourceManager.GetString("PackageInstallationError", resourceCulture); } } /// /// Looks up a localized string similar to Unable to read package source file. Ensure that the file "{0}" is writeable and the contents of the file have not been modified externally.. /// internal static string PackageSourceFileInstructions { get { return ResourceManager.GetString("PackageSourceFileInstructions", resourceCulture); } } /// /// Looks up a localized string similar to Source. /// internal static string PackageSourceLabel { get { return ResourceManager.GetString("PackageSourceLabel", resourceCulture); } } /// /// Looks up a localized string similar to Error occurred uninstalling package "{0}":. /// internal static string PackageUninstallationError { get { return ResourceManager.GetString("PackageUninstallationError", resourceCulture); } } /// /// Looks up a localized string similar to Error occurred updating package "{0}":. /// internal static string PackageUpdateError { get { return ResourceManager.GetString("PackageUpdateError", resourceCulture); } } /// /// Looks up a localized string similar to Page. /// internal static string PageLabel { get { return ResourceManager.GetString("PageLabel", resourceCulture); } } /// /// Looks up a localized string similar to Previous. /// internal static string PreviousText { get { return ResourceManager.GetString("PreviousText", resourceCulture); } } /// /// Looks up a localized string similar to Privacy Statement. /// internal static string PrivacyStatement { get { return ResourceManager.GetString("PrivacyStatement", resourceCulture); } } /// /// Looks up a localized string similar to Remove Dependencies?. /// internal static string RemoveDependencies { get { return ResourceManager.GetString("RemoveDependencies", resourceCulture); } } /// /// Looks up a localized string similar to Restore Default . /// internal static string RestoreDefaultSources { get { return ResourceManager.GetString("RestoreDefaultSources", resourceCulture); } } /// /// Looks up a localized string similar to Search. /// internal static string SearchLabel { get { return ResourceManager.GetString("SearchLabel", resourceCulture); } } /// /// Looks up a localized string similar to Packages. /// internal static string SectionTitle { get { return ResourceManager.GetString("SectionTitle", resourceCulture); } } /// /// Looks up a localized string similar to Show. /// internal static string ShowLabel { get { return ResourceManager.GetString("ShowLabel", resourceCulture); } } /// /// Looks up a localized string similar to Name. /// internal static string SourceNameLabel { get { return ResourceManager.GetString("SourceNameLabel", resourceCulture); } } /// /// Looks up a localized string similar to Source. /// internal static string SourceUrlLabel { get { return ResourceManager.GetString("SourceUrlLabel", resourceCulture); } } /// /// Looks up a localized string similar to Uninstall. /// internal static string UninstallPackage { get { return ResourceManager.GetString("UninstallPackage", resourceCulture); } } /// /// Looks up a localized string similar to Uninstalling package {0}.. /// internal static string UninstallPackageDesc { get { return ResourceManager.GetString("UninstallPackageDesc", resourceCulture); } } /// /// Looks up a localized string similar to Package <strong>{0}</strong> was successfully uninstalled.. /// internal static string UninstallSuccess { get { return ResourceManager.GetString("UninstallSuccess", resourceCulture); } } /// /// Looks up a localized string similar to Unknown framework assembly: "{0}". /// internal static string UnknownFrameworkReference { get { return ResourceManager.GetString("UnknownFrameworkReference", resourceCulture); } } /// /// Looks up a localized string similar to Update. /// internal static string UpdatePackage { get { return ResourceManager.GetString("UpdatePackage", resourceCulture); } } /// /// Looks up a localized string similar to Updating package {0} to version {1}.. /// internal static string UpdatePackageDesc { get { return ResourceManager.GetString("UpdatePackageDesc", resourceCulture); } } /// /// Looks up a localized string similar to Updates. /// internal static string UpdatesLabel { get { return ResourceManager.GetString("UpdatesLabel", resourceCulture); } } /// /// Looks up a localized string similar to Package <strong>{0}</strong> was successfully updated.. /// internal static string UpdateSuccess { get { return ResourceManager.GetString("UpdateSuccess", resourceCulture); } } /// /// Looks up a localized string similar to Invalid source url.. /// internal static string Validation_InvalidPackageSourceUrl { get { return ResourceManager.GetString("Validation_InvalidPackageSourceUrl", resourceCulture); } } /// /// Looks up a localized string similar to Specified package source already exists.. /// internal static string Validation_PackageSourceAlreadyExists { get { return ResourceManager.GetString("Validation_PackageSourceAlreadyExists", resourceCulture); } } /// /// Looks up a localized string similar to View License Terms. /// internal static string ViewLicenseTerms { get { return ResourceManager.GetString("ViewLicenseTerms", resourceCulture); } } /// /// Looks up a localized string similar to , . /// internal static string WordSeparator { get { return ResourceManager.GetString("WordSeparator", resourceCulture); } } } } ================================================ FILE: src/System.Web.WebPages.Administration/Resources/PackageManagerResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Source Page Install Default No packages found. Error installing package "{0}": Error occurred uninstalling package "{0}": Search Uninstall Bad Request A collection of tools to automate the process of installing, upgrading, configuring, and removing packages from an ASP.NET application. Package Manager No packages installed. <p><a href="{0}">Install packages from an online feed</a></p> Not HTML encoding the text. Ensure params are encoded Author , String used as glue to join multiple words into a single comma separated string By clicking "Install" you agree to the license terms for the package(s) above. If you do not agree to the license terms, click "Cancel." Each package is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages. Installing package {0}. Uninstalling package {0}. Are you sure you want to uninstall <strong>{0}</strong>? Not HTML encoding the text. Ensure params are encoded Cancel Package <strong>{0}</strong> was successfully uninstalled. Not HTML encoding the text. Ensure params are encoded Update JavaScript is required to correctly view this page. View License Terms Package <strong>{0}</strong> was successfully installed. Not HTML encoding the text. Ensure params are encoded Updating package {0} to version {1}. Updating Foo 1.1 to version 1.3. Package <strong>{0}</strong> was successfully updated. Not HTML encoding the text. Ensure params are encoded Clear Use this page to manage your package sources. Add Package Source Delete Name Source Manage Package Sources Invalid source url. Unable to read package source file. Ensure that the file "{0}" is writeable and the contents of the file have not been modified externally. Restore Default Error occurred updating package "{0}": Remove Dependencies? Packages Specified package source already exists. Show Installed Online Updates Default (All) Privacy Statement Unknown framework assembly: "{0}" Next Previous ================================================ FILE: src/System.Web.WebPages.Administration/Site.css ================================================ /* Reset ***************************************************************/ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } /* Remember focus styles! */ :focus { outline: 0; } body { line-height: 1.4em; color: black; background: white; } ol, ul { list-style: none; } /* Tables still need 'cellspacing="0"' in the markup */ table { border-collapse: separate; border-spacing: 0; } caption, td { text-align: left; } thead th { font-weight: bold; text-align: center; } blockquote:before, blockquote:after, q:before, q:after { content: ""; } blockquote, q { quotes: "" ""; } /* HTML 5 elements as block */ header, footer, aside, nav, article { display: block; } /* Clearing Floats ***************************************************************/ .group:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } /* General ***************************************************************/ /* Default font settings. The font-size 81.3% sets the base font to 13px Pixels EMs Percent Points 6px 0.462em 46.2% 5pt 7px 0.538em 53.8% 5pt 8px 0.615em 61.5% 6pt 9px 0.692em 69.2% 7pt 10px 0.769em 76.9% 8pt 11px 0.846em 84.6% 8pt 12px 0.923em 92.3% 9pt 13px 1em 100% 10pt 14px 1.077em 107.7% 11pt 15px 1.154em 115.4% 11pt 16px 1.231em 123.1% 12pt 17px 1.308em 130.8% 13pt 18px 1.385em 138.5% 14pt 19px 1.462em 146.2% 14pt 20px 1.538em 153.8% 15pt 21px 1.615em 161.5% 16pt 22px 1.692em 169.2% 17pt 23px 1.769em 176.9% 17pt 24px 1.846em 184.6% 18pt 26px 2em 200% 20pt */ body { background-color: #ececec; font-size: 81.3%; font-family: Segoe UI,Trebuchet,"Helvetica Neue", Arial, Helvetica, sans-serif; color:#333; margin: 0; padding: 0; /*CSS3 properties*/ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#ececec'); background: -webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#ececec)) fixed; background: -moz-linear-gradient(top, #fff, #ececec) fixed; } a:link { color: #034af3; text-decoration: underline; } a:visited { color: #505abc; } a:hover { color: #1d60ff; text-decoration: none; } a:active { color: #12eb87; } /* Headings and Text Elements ***************************************************************/ /* Headings */ h1, h2, h3, h4, h5, h6 { color: #385366; } h1 { font-size: 184.6%; } /*24px*/ h2 { font-size: 176.9%; } /*23px*/ h3 { font-size: 153.8%; } /*20px*/ h4 { font-size: 138.5%; } /*18px*/ h5 { font-size: 123.1%; } /*16px*/ h6 { font-size: 107.7%; } /*14px*/ /* Text elements */ p { line-height: 18px; margin-top: 10px; } .text-small { font-size: 84.6%; /*11px*/ } strong { font-weight: bold; } code { display: block; margin: 5px 0px 5px 5px; } em { font-style: italic; } /* Layout ***************************************************************/ #page { width: 90%; margin-left: auto; margin-right: auto; } #header { position: relative; margin-bottom: 0; color: #000; padding: 26px 26px 26px 0; } #main { padding: 30px 30px 15px 30px; background-color: #fff; margin: 0 0 30px 0; border: 1px solid #a6a6a6; /*CSS3 properties*/ border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; -webkit-box-shadow: 0px 0px 4px rgba(102, 102, 102, 0.3); -moz-box-shadow: 0px 0px 4px rgba(102, 102, 102, 0.3); box-shadow: 0px 0px 4px rgba(102, 102, 102, 0.3); background: -webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#f3f3f3)) fixed; background: -moz-linear-gradient(top, #fff, #f3f3f3) fixed; filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#f3f3f3'); } #footer { padding: 10px 0; text-align: center; line-height: normal; margin: 0; } /* Site Title ***************************************************************/ #header .site-title { float: left; color: #385366; padding: 5px; margin: 0; border: none; } #header .site-title span.aspnet { color:#3c89c8; } /* Login Display ***************************************************************/ #settings { float: right; font-size: 123.1%; /*16px*/ display: block; text-align: right; margin: 14px 0 0 0; } #settings li { margin: 0; display: inline; list-style: none; padding: 0 0 0 5px; } #settings a:before { margin-right: 10px; content: "[" } #settings a:after { margin-left: 10px; content: "]" } #settings a:link, #settings a:visited { text-decoration: none; margin: 0 0 0 10px; color:#034AF3; } #settings a:hover { text-decoration: underline; } /* Tab Menu ***************************************************************/ .nav li { font-size: 123.1%; } /*16px*/ ul#menu { clear: both; border-bottom: 1px #5c87b2 solid; padding: 0 0 2px 0; position: relative; text-align: right; } ul#menu li { display: inline; list-style: none; } ul#menu li a { padding: 10px 20px 0 0; font-weight: bold; text-decoration: none; line-height: 2.8em; background-color: #e8eef4; color: #034af3; /*CSS3 properties*/ border-radius: 4px 4px 0 0; -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; } ul#menu li a:hover { background-color: #fff; text-decoration: none; } ul#menu li a:active { background-color: #a6e2a6; text-decoration: none; } ul#menu li.selected a { background-color: #fff; color: #000; } /* Page Title ***************************************************************/ .page-title { border-bottom: 1px solid #e8e6e6; margin: 0 0 26px 0; } /* Forms ***************************************************************/ fieldset { margin: 10px 0; padding: 10px; border: 1px solid #ccc; } fieldset legend { font-size: 123.1%; /*16px*/ font-weight: 600; padding: 2px 4px 8px 4px; } fieldset ol { padding: 0; list-style: none; } fieldset ol li { padding: 0 0 5px 0; } fieldset label { display: block; font-weight: bold; } fieldset label.checkbox { display: inline; } select, fieldset input[type="text"], input[type="password"] { border: 1px solid #c4c4c4; color: #444; width: 300px; padding: 3px; /*CSS3 properties*/ border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } select { width: auto; } input[type="submit"], input[type="reset"], input[type="button"] { font-size: 107.7%; /*14px*/ color:#333; background:#F5F5F5; border:1px solid #999; cursor:pointer; width:80px; padding: 1px; text-align:center; /*CSS3 properties*/ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr='#f5f5f5', endColorstr='#cbcbcb'); background: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#cbcbcb)); background: -moz-linear-gradient(top, #f5f5f5, #cbcbcb); box-shadow: inset 0px 0px 1px rgba(255, 255, 255, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); -webkit-box-shadow: inset 0px 0px 1px rgba(255, 255, 255, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); -moz-box-shadow: inset 0px 0px 1px rgba(255, 255, 255, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } input[type="submit"]:hover, input[type="reset"]:hover, input[type="button"]:hover, button:hover { text-decoration:none; background: #62a9e2; color:#fff; border:1px solid #2e76b1; /*CSS3 properties*/ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr='#62a9e2', endColorstr='#3c89c8'); background: -webkit-gradient(linear, 0 0, 0 100%, from(#62a9e2), to(#3c89c8)); background: -moz-linear-gradient(top, #62a9e2, #3c89c8); } input[type="submit"]:active, input[type="reset"]:active, input[type="button"]:active, button:active { text-decoration:none; background: #62a9e2; color:#fff; border:1px solid #093253; /*CSS3 properties*/ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr='#72b8f2', endColorstr='#3078b3'); background: -webkit-gradient(linear, 0 0, 0 100%, from(#72b8f2), to(#3078b3)); background: -moz-linear-gradient(top, #72b8f2, #3078b3); box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); -moz-box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); -webkit-box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); } input[type="submit"].long-input, input[type="reset"].long-input, input[type="button"].long-input, button.long-input { width: 140px; } input[type="submit"]:focus::-moz-focus-inner, button:focus::-moz-focus-inner { border: 1px dotted transparent; } /* Information and errors ----------------------------------------------------------*/ .message { clear: both; border: 1px solid; margin: 10px 0px; padding: 15px; font-weight: bold; /*CSS3 properties*/ border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; -moz-box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); -webkit-box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 1.0), 1px 1px 1px rgba(102, 102, 102, 0.3); } .info { background: #dddddd; color: #00529b; } .error { background: #ffe4e4; color: #d63301; } .success { background: #dff2bf; color: #43750E; } input[type="text"].validation-error, input[type="password"].validation-error { border: solid 1px #d63301; background-color: #ffccba; font-weight: inherit; font-size: inherit; color: inherit; } .validation-error { display: inline; color: #be3e16; font-weight: 600; font-size: 123.1%; /*16px*/ } .page-title > h1 { margin-top: 0; } .page-title > span { font-style: italic; } .modules { list-style-type: none; margin: 0 0 0 -15px; } .modules > li { background: url(images/package.png) no-repeat 0 10%; padding: 0 0 0 30px; } .modules > li > a { font-size: 123.1%; /*16px*/ } #breadcrumbs { font-size: 123.1%; /*16px*/ margin:0 auto; display: block; height: 44px; } #breadcrumbs ul, #breadcrumbs li { float:left; margin:0 8px; height: 100%; } #breadcrumbs ul { line-height: 20px; list-style: none outside none; padding: 0; } #breadcrumbs li.selected { background:url("images/tabOn.gif") no-repeat scroll 45% bottom transparent; bottom: -1px; position: relative; } #breadcrumbs a, #breadcrumbs a:visited, #breadcrumbs a:active { text-decoration: none; color:#034AF3; } .error ul { padding: 0; margin: 0; } .error ul li { list-style-type: none; } .page-settings { float: right; width: 5%; text-transform: lowercase; } hr { border-top: none; border-left: none; border-right: none; border-bottom: 1px solid #E8E6E6; } /* Misc ***************************************************************/ .clear { clear: both; } .left { float: left; } .centered { text-align: center; } .right { float: right; } img.inline { vertical-align: text-bottom; } ================================================ FILE: src/System.Web.WebPages.Administration/System.Web.WebPages.Administration.csproj ================================================  {C23F02FC-4538-43F5-ABBA-38BA069AEA8F} Library Properties System.Web.WebPages.Administration System.Web.WebPages.Administration $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 True ..\..\packages\Nuget.Core.1.6.2\lib\net40\NuGet.Core.dll Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True PackageManagerResources.resx True True AdminResources.resx Properties\CommonAssemblyInfo.cs Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator AdminResources.Designer.cs System.Web.WebPages.Administration ResXFileCodeGenerator PackageManagerResources.Designer.cs System.Web.WebPages.Administration.PackageManager {22BABB60-8F02-4027-AFFC-ACF069954536} System.Web.WebPages.Deployment {4D39BAAF-8A96-473E-AB79-C8A341885137} WebMatrix.Data {9B7E3740-6161-4548-833C-4BBCA43B970E} System.Web.Helpers {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {55A15F40-1435-4248-A7F2-2A146BB83586} WebMatrix.WebData System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager System.Web.WebPages.Administration.PackageManager ================================================ FILE: src/System.Web.WebPages.Administration/_Layout.cshtml ================================================ @* Generator: WebPage *@ @{ string title = Page.Title; } @AdminResources.AdminModuleTitle@if (!title.IsEmpty()) { <text>- @title </text> } @RenderSection("Head", required: false)
@{ string sectionTitle = Page.SectionTitle ?? title; } @if (!sectionTitle.IsEmpty()) {

@sectionTitle

@if (!String.IsNullOrEmpty(Page.Desc)) { @Page.Desc }
} @RenderBody()

================================================ FILE: src/System.Web.WebPages.Administration/_pagestart.cshtml ================================================ @* Generator: WebPage *@ @{ // 404 if the admin page isn't available if (!SiteAdmin.Available) { Response.SetStatus(HttpStatusCode.NotFound); return; } AdminSecurity.Authorize(this); } @{ // Set up layout values var breadCrumbs = new List>(); if (SiteAdmin.Modules.Any()) { breadCrumbs.Add(Tuple.Create(AdminResources.AdminHome, Href(SiteAdmin.AdminVirtualPath))); } PageData["BreadCrumbs"] = breadCrumbs; Layout = "_Layout.cshtml"; HtmlHelper.ValidationSummaryClass = "message error"; HtmlHelper.ValidationInputCssClassName = "validation-error"; // Force IE9 standards mode rendering Response.AddHeader("X-UA-Compatible", "IE=Edge"); } ================================================ FILE: src/System.Web.WebPages.Administration/packages.config ================================================  ================================================ FILE: src/System.Web.WebPages.Deployment/AppDomainHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; namespace System.Web.WebPages.Deployment { internal static class AppDomainHelper { public static IDictionary> GetBinAssemblyReferences(string appPath, string configPath) { string binDirectory = Path.Combine(appPath, "bin"); if (!Directory.Exists(binDirectory)) { return null; } AppDomain appDomain = null; try { var appDomainSetup = new AppDomainSetup { ApplicationBase = appPath, ConfigurationFile = configPath, PrivateBinPath = binDirectory, }; appDomain = AppDomain.CreateDomain(typeof(AppDomainHelper).Namespace, AppDomain.CurrentDomain.Evidence, appDomainSetup); var type = typeof(RemoteAssemblyLoader); var instance = (RemoteAssemblyLoader)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); return Directory.EnumerateFiles(binDirectory, "*.dll") .ToDictionary(assemblyPath => assemblyPath, assemblyPath => instance.GetReferences(assemblyPath)); } finally { if (appDomain != null) { AppDomain.Unload(appDomain); } } } private sealed class RemoteAssemblyLoader : MarshalByRefObject { [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Method needs to be instance level for cross domain invocation"), SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom", Justification = "We want to load this specific assembly.")] public IEnumerable GetReferences(string assemblyPath) { var assembly = Assembly.LoadFrom(assemblyPath); return assembly.GetReferencedAssemblies() .Select(asmName => Assembly.Load(asmName.FullName).FullName) .Concat(new[] { assembly.FullName }) .ToArray(); } } } } ================================================ FILE: src/System.Web.WebPages.Deployment/AssemblyUtils.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Security; using Microsoft.Internal.Web.Utils; using Microsoft.Web.Infrastructure; namespace System.Web.WebPages.Deployment { internal static class AssemblyUtils { // Copied from AssemblyRefs.cs private const string SharedLibPublicKey = "31bf3856ad364e35"; internal static readonly AssemblyName ThisAssemblyName = new AssemblyName(typeof(AssemblyUtils).Assembly.FullName); internal static readonly Version WebPagesV1Version = new Version(1, 0, 0, 0); private static readonly string _binFileName = Path.GetFileName(ThisAssemblyName.Name) + ".dll"; // Special case MWI because it does not share the same assembly version as the rest of WebPages. private static readonly Version _mwiVersion = new AssemblyName(typeof(InfrastructureHelper).Assembly.FullName).Version; private static readonly AssemblyName _mwiAssemblyName = GetFullName("Microsoft.Web.Infrastructure", _mwiVersion); private static readonly AssemblyName[] _version1AssemblyList = new[] { _mwiAssemblyName, GetFullName("System.Web.Razor", WebPagesV1Version), GetFullName("System.Web.Helpers", WebPagesV1Version), GetFullName("System.Web.WebPages", WebPagesV1Version), GetFullName("System.Web.WebPages.Administration", WebPagesV1Version), GetFullName("System.Web.WebPages.Razor", WebPagesV1Version), GetFullName("WebMatrix.Data", WebPagesV1Version), GetFullName("WebMatrix.WebData", WebPagesV1Version) }; private static readonly AssemblyName[] _versionCurrentAssemblyList = new[] { _mwiAssemblyName, GetFullName("System.Web.Razor", ThisAssemblyName.Version), GetFullName("System.Web.Helpers", ThisAssemblyName.Version), GetFullName("System.Web.WebPages", ThisAssemblyName.Version), GetFullName("System.Web.WebPages.Administration", ThisAssemblyName.Version), GetFullName("System.Web.WebPages.Razor", ThisAssemblyName.Version), GetFullName("WebMatrix.Data", ThisAssemblyName.Version), GetFullName("WebMatrix.WebData", ThisAssemblyName.Version) }; internal static Version GetMaxWebPagesVersion() { return GetMaxWebPagesVersion(GetLoadedAssemblies()); } internal static Version GetMaxWebPagesVersion(IEnumerable loadedAssemblies) { return GetWebPagesAssemblies(loadedAssemblies).Max(c => c.Version); } internal static bool IsVersionAvailable(Version version) { return IsVersionAvailable(GetLoadedAssemblies(), version); } internal static bool IsVersionAvailable(IEnumerable loadedAssemblies, Version version) { return GetWebPagesAssemblies(loadedAssemblies).Any(c => c.Version == version); } private static IEnumerable GetWebPagesAssemblies(IEnumerable loadedAssemblies) { return (from otherName in loadedAssemblies where NamesMatch(ThisAssemblyName, otherName, matchVersion: false) select otherName); } /// /// Returns the version of a System.Web.WebPages.Deployment.dll if it is present in the bin and matches the name and /// public key token of the current assembly. /// /// Version from bin if present, null otherwise. internal static Version GetVersionFromBin(string binDirectory, IFileSystem fileSystem, Func getAssemblyNameThunk = null) { // If a version of the assembly is present both in the bin and the GAC, the GAC would win. // To work around this, we'll look for a physical file on disk with the same name as the current assembly and load it to determine the version. // Determine if the Deployment assembly is present in the bin var assemblyInBin = Path.Combine(binDirectory, _binFileName); if (fileSystem.FileExists(assemblyInBin)) { try { getAssemblyNameThunk = getAssemblyNameThunk ?? AssemblyName.GetAssemblyName; AssemblyName assemblyName = getAssemblyNameThunk(assemblyInBin); if (NamesMatch(ThisAssemblyName, assemblyName, matchVersion: false)) { return assemblyName.Version; } } catch (BadImageFormatException) { // Do nothing. } catch (SecurityException) { // Do nothing } catch (FileLoadException) { // Do nothing. } } return null; } internal static bool NamesMatch(AssemblyName left, AssemblyName right, bool matchVersion) { return Equals(left.Name, right.Name) && Equals(left.CultureInfo, right.CultureInfo) && Enumerable.SequenceEqual(left.GetPublicKeyToken(), right.GetPublicKeyToken()) && (!matchVersion || Equals(left.Version, right.Version)); } internal static IEnumerable GetLoadedAssemblies() { return AppDomain.CurrentDomain.GetAssemblies() .Select(GetAssemblyName) .ToList(); } internal static IEnumerable GetAssembliesForVersion(Version version) { if (version == WebPagesV1Version) { return _version1AssemblyList; } return _versionCurrentAssemblyList; } private static AssemblyName GetAssemblyName(Assembly assembly) { return new AssemblyName(assembly.FullName); } private static AssemblyName GetFullName(string name, Version version, string publicKeyToken) { return new AssemblyName(String.Format(CultureInfo.InvariantCulture, "{0}, Version={1}, Culture=neutral, PublicKeyToken={2}", name, version, publicKeyToken)); } internal static AssemblyName GetFullName(string name, Version version) { return GetFullName(name, version, SharedLibPublicKey); } public static IDictionary GetAssembliesMatchingOtherVersions(IDictionary> references) { var webPagesAssemblies = AssemblyUtils.GetAssembliesForVersion(AssemblyUtils.ThisAssemblyName.Version); if (references == null || webPagesAssemblies == null || !webPagesAssemblies.Any()) { return new Dictionary(0); } var matchingVersions = from item in references let matchedVersion = GetMatchingVersion(webPagesAssemblies, item.Value) where matchedVersion != null select new KeyValuePair(item.Key, matchedVersion); return matchingVersions.ToDictionary(k => k.Key, k => k.Value); } private static Version GetMatchingVersion(IEnumerable webPagesAssemblies, IEnumerable references) { // Return assemblies that match in name but not in version. var matchingVersions = from webPagesAssembly in webPagesAssemblies from referenceName in references let referencedAssembly = new AssemblyName(referenceName) where AssemblyUtils.NamesMatch(webPagesAssembly, referencedAssembly, matchVersion: false) && webPagesAssembly.Version != referencedAssembly.Version select referencedAssembly.Version; return matchingVersions.FirstOrDefault(); } } } ================================================ FILE: src/System.Web.WebPages.Deployment/BuildManagerWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Web.Compilation; namespace System.Web.WebPages.Deployment { internal sealed class BuildManagerWrapper : IBuildManager { /// /// Reads a special cached file from %WindDir%\Microsoft.NET\Framework\vx.x\ASP.NET Temporary Files\<x>\<y>\UserCache that is /// available across AppDomain recycles. /// public Stream ReadCachedFile(string path) { return BuildManager.ReadCachedFile(path); } /// /// Creates or opens a special cached file that is created under %WindDir%\Microsoft.NET\Framework\vx.x\ASP.NET Temporary Files\<x>\<y>\UserCache that is /// available across AppDomain recycles. /// public Stream CreateCachedFile(string path) { return BuildManager.CreateCachedFile(path); } } } ================================================ FILE: src/System.Web.WebPages.Deployment/Common/IFileSystem.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; namespace Microsoft.Internal.Web.Utils { internal interface IFileSystem { bool FileExists(string path); Stream ReadFile(string path); Stream OpenFile(string path); IEnumerable EnumerateFiles(string root); } } ================================================ FILE: src/System.Web.WebPages.Deployment/Common/PhysicalFileSystem.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; namespace Microsoft.Internal.Web.Utils { internal sealed class PhysicalFileSystem : IFileSystem { public bool FileExists(string path) { return File.Exists(path); } public Stream ReadFile(string path) { return File.OpenRead(path); } public Stream OpenFile(string path) { string directory = Path.GetDirectoryName(path); EnsureDirectory(directory); return File.OpenWrite(path); } public IEnumerable EnumerateFiles(string path) { return Directory.EnumerateFiles(path); } private static void EnsureDirectory(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } } } ================================================ FILE: src/System.Web.WebPages.Deployment/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Web.WebPages.Deployment", Justification = "Namespace is present to match assembly name")] ================================================ FILE: src/System.Web.WebPages.Deployment/IBuildManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; namespace System.Web.WebPages.Deployment { internal interface IBuildManager { Stream CreateCachedFile(string fileName); Stream ReadCachedFile(string fileName); } } ================================================ FILE: src/System.Web.WebPages.Deployment/PreApplicationStartCode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Caching; using System.Web.Compilation; using System.Web.Configuration; using System.Web.WebPages.Deployment.Resources; using Microsoft.Internal.Web.Utils; using Microsoft.Web.Infrastructure; namespace System.Web.WebPages.Deployment { [EditorBrowsable(EditorBrowsableState.Never)] public static class PreApplicationStartCode { /// /// Key used to indicate to tooling that the compile exception we throw to refresh the app domain originated from us so that they can deal with it correctly. /// private const string ToolingIndicatorKey = "WebPages.VersionChange"; // NOTE: Do not add public fields, methods, or other members to this class. // This class does not show up in Intellisense so members on it will not be // discoverable by users. Place new members on more appropriate classes that // relate to the public API (for example, a LoginUrl property should go on a // membership-related class). private static readonly IFileSystem _physicalFileSystem = new PhysicalFileSystem(); private static bool _startWasCalled; public static void Start() { // Even though ASP.NET will only call each PreAppStart once, we sometimes internally call one PreAppStart from // another PreAppStart to ensure that things get initialized in the right order. ASP.NET does not guarantee the // order so we have to guard against multiple calls. // All Start calls are made on same thread, so no lock needed here. if (_startWasCalled) { return; } _startWasCalled = true; StartCore(); } internal static bool StartCore() { var buildManager = new BuildManagerWrapper(); NameValueCollection appSettings = WebConfigurationManager.AppSettings; Action loadWebPages = LoadWebPages; Action registerForChangeNotification = RegisterForChangeNotifications; IEnumerable loadedAssemblies = AssemblyUtils.GetLoadedAssemblies(); return StartCore(_physicalFileSystem, HttpRuntime.AppDomainAppPath, HttpRuntime.BinDirectory, appSettings, loadedAssemblies, buildManager, loadWebPages, registerForChangeNotification); } // Adds Parameter for unit tests internal static bool StartCore(IFileSystem fileSystem, string appDomainAppPath, string binDirectory, NameValueCollection appSettings, IEnumerable loadedAssemblies, IBuildManager buildManager, Action loadWebPages, Action registerForChangeNotification, Func getAssemblyNameThunk = null) { if (WebPagesDeployment.IsExplicitlyDisabled(appSettings)) { // If WebPages is explicitly disabled, exit. Debug.WriteLine("WebPages Bootstrapper v{0}: not loading WebPages since it is disabled", AssemblyUtils.ThisAssemblyName.Version); return false; } Version maxWebPagesVersion = AssemblyUtils.GetMaxWebPagesVersion(loadedAssemblies); Debug.Assert(maxWebPagesVersion != null, "Function must return some max value."); if (AssemblyUtils.ThisAssemblyName.Version != maxWebPagesVersion) { // Always let the highest version determine what needs to be done. This would make future proofing simpler. Debug.WriteLine("WebPages Bootstrapper v{0}: Higher version v{1} is available.", AssemblyUtils.ThisAssemblyName.Version, maxWebPagesVersion); return false; } var webPagesEnabled = WebPagesDeployment.IsEnabled(fileSystem, appDomainAppPath, appSettings); Version binVersion = AssemblyUtils.GetVersionFromBin(binDirectory, fileSystem, getAssemblyNameThunk); Version configVersion = WebPagesDeployment.GetVersionFromConfig(appSettings); Version version = configVersion ?? binVersion ?? AssemblyUtils.WebPagesV1Version; // Asserts to ensure unit tests are set up correctly. So essentially, we're unit testing the unit tests. Debug.Assert(version != null, "GetVersion always returns a version"); Debug.Assert(binVersion == null || binVersion <= maxWebPagesVersion, "binVersion cannot be higher than max version"); if ((binVersion != null) && (binVersion != version)) { // Determine if there's a version conflict. A conflict could occur if there's a version specified in the bin which is different from the version specified in the // config that is different. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ConfigurationResources.WebPagesVersionConflict, version, binVersion)); } else if (binVersion != null) { // The rest of the code is only meant to be executed if we are executing from the GAC. // If a version is bin deployed, we don't need to do anything special to bootstrap. return false; } else if (!webPagesEnabled) { Debug.WriteLine("WebPages Bootstrapper v{0}: WebPages not enabled, registering for change notifications", AssemblyUtils.ThisAssemblyName.Version); // Register for change notifications under the application root registerForChangeNotification(); return false; } else if (!AssemblyUtils.IsVersionAvailable(loadedAssemblies, version)) { if (version == AssemblyUtils.WebPagesV1Version && configVersion == null && binVersion == null) { // No version was specified. We're implicitly assuming that the site is a v1 site. However, the user does not have V1 binaries available. throw new InvalidOperationException(ConfigurationResources.WebPagesImplicitVersionFailure); } else { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ConfigurationResources.WebPagesVersionNotFound, version, AssemblyUtils.ThisAssemblyName.Version)); } } Debug.WriteLine("WebPages Bootstrapper v{0}: loading version {1}, loading WebPages", AssemblyUtils.ThisAssemblyName.Version, version); // If the version the application was compiled earlier was different, invalidate compilation results by adding a file to the bin. InvalidateCompilationResultsIfVersionChanged(buildManager, fileSystem, binDirectory, version); loadWebPages(version); return true; } /// /// WebPages stores the version to be compiled against in AppSettings as >add key="webpages:version" value="1.0" /<. /// Changing values AppSettings does not cause recompilation therefore we could run into a state where we have files compiled against v1 but the application is /// currently v2. /// private static void InvalidateCompilationResultsIfVersionChanged(IBuildManager buildManager, IFileSystem fileSystem, string binDirectory, Version currentVersion) { Version previousVersion = WebPagesDeployment.GetPreviousRuntimeVersion(buildManager); // Persist the current version number in BuildManager's cached file WebPagesDeployment.PersistRuntimeVersion(buildManager, currentVersion); if (previousVersion == null) { // Do nothing. } else if (previousVersion != currentVersion) { // If the previous runtime version is different, perturb the bin directory so that it forces recompilation. WebPagesDeployment.ForceRecompile(fileSystem, binDirectory); var httpCompileException = new HttpCompileException(ConfigurationResources.WebPagesVersionChanges); // Indicator for tooling httpCompileException.Data[ToolingIndicatorKey] = true; throw httpCompileException; } } // Copied from xsp\System\Web\Compilation\BuildManager.cs [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Copied from System.Web.dll")] internal static ICollection GetPreStartInitMethodsFromAssemblyCollection(IEnumerable assemblies) { List methods = new List(); foreach (Assembly assembly in assemblies) { PreApplicationStartMethodAttribute[] attributes = null; try { attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true); } catch { // GetCustomAttributes invokes the constructors of the attributes, so it is possible that they might throw unexpected exceptions. // (Dev10 bug 831981) } if (attributes != null && attributes.Length != 0) { Debug.Assert(attributes.Length == 1); PreApplicationStartMethodAttribute attribute = attributes[0]; Debug.Assert(attribute != null); MethodInfo method = null; // Ensure the Type on the attribute is in the same assembly as the attribute itself if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) { method = FindPreStartInitMethod(attribute.Type, attribute.MethodName); } if (method != null) { methods.Add(method); } // No-op if the attribute is invalid /* else { throw new HttpException(SR.GetString(SR.Invalid_PreApplicationStartMethodAttribute_value, assembly.FullName, (attribute.Type != null ? attribute.Type.FullName : String.Empty), attribute.MethodName)); } */ } } return methods; } // Copied from xsp\System\Web\Compilation\BuildManager.cs internal static MethodInfo FindPreStartInitMethod(Type type, string methodName) { Debug.Assert(type != null); Debug.Assert(!String.IsNullOrEmpty(methodName)); MethodInfo method = null; if (type.IsPublic) { // Verify that type is public to avoid allowing internal code execution. This implementation will not match // nested public types. method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase, binder: null, types: Type.EmptyTypes, modifiers: null); } return method; } [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The cache disposes of the dependency")] private static void RegisterForChangeNotifications() { string physicalPath = HttpRuntime.AppDomainAppPath; CacheDependency cacheDependency = new CacheDependency(physicalPath, DateTime.UtcNow); var key = WebPagesDeployment.CacheKeyPrefix + physicalPath; HttpRuntime.Cache.Insert(key, physicalPath, cacheDependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(OnChanged)); } private static void OnChanged(string key, object value, CacheItemRemovedReason reason) { // Only handle case when the dependency has changed. if (reason != CacheItemRemovedReason.DependencyChanged) { return; } // Scan the app root for a webpages file if (WebPagesDeployment.AppRootContainsWebPagesFile(_physicalFileSystem, HttpRuntime.AppDomainAppPath)) { // Unload the app domain so we register plan9 when the app restarts InfrastructureHelper.UnloadAppDomain(); } else { // We need to re-register since the item was removed from the cache RegisterForChangeNotifications(); } } private static void LoadWebPages(Version version) { IEnumerable assemblyList = AssemblyUtils.GetAssembliesForVersion(version); var assemblies = assemblyList.Select(LoadAssembly); foreach (var asm in assemblies) { BuildManager.AddReferencedAssembly(asm); } foreach (var m in GetPreStartInitMethodsFromAssemblyCollection(assemblies)) { m.Invoke(null, null); } } private static Assembly LoadAssembly(AssemblyName name) { return Assembly.Load(name); } } } ================================================ FILE: src/System.Web.WebPages.Deployment/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using System.Web.WebPages.Deployment; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("System.Web.WebPages.Deployment")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("System.Web.WebPages.Deployment.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] ================================================ FILE: src/System.Web.WebPages.Deployment/Resources/ConfigurationResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.269 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.WebPages.Deployment.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class ConfigurationResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal ConfigurationResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.WebPages.Deployment.Resources.ConfigurationResources", typeof(ConfigurationResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to The "InstallPath" name was not found in the Web Pages registry key "{0}".. /// internal static string InstallPathNotFound { get { return ResourceManager.GetString("InstallPathNotFound", resourceCulture); } } /// /// Looks up a localized string similar to Could not determine which version of ASP.NET Web Pages to use. /// ///In order to use this site, specify a version in the site’s web.config file. For more information, see the following article on the Microsoft support site: http://go.microsoft.com/fwlink/?LinkId=254126. /// internal static string WebPagesImplicitVersionFailure { get { return ResourceManager.GetString("WebPagesImplicitVersionFailure", resourceCulture); } } /// /// Looks up a localized string similar to The Web Pages registry key "{0}" does not exist.. /// internal static string WebPagesRegistryKeyDoesNotExist { get { return ResourceManager.GetString("WebPagesRegistryKeyDoesNotExist", resourceCulture); } } /// /// Looks up a localized string similar to Changes were detected in the Web Pages runtime version that require your application to be recompiled. Refresh your browser window to continue.. /// internal static string WebPagesVersionChanges { get { return ResourceManager.GetString("WebPagesVersionChanges", resourceCulture); } } /// /// Looks up a localized string similar to Conflicting versions of ASP.NET Web Pages detected: specified version is "{0}", but the version in bin is "{1}". To continue, remove files from the application's bin directory or remove the version specification in web.config.. /// internal static string WebPagesVersionConflict { get { return ResourceManager.GetString("WebPagesVersionConflict", resourceCulture); } } /// /// Looks up a localized string similar to Specified Web Pages version "{0}" could not be found. Update your web.config to specify a different version. Current version: "{1}".. /// internal static string WebPagesVersionNotFound { get { return ResourceManager.GetString("WebPagesVersionNotFound", resourceCulture); } } } } ================================================ FILE: src/System.Web.WebPages.Deployment/Resources/ConfigurationResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 The "InstallPath" name was not found in the Web Pages registry key "{0}". Could not determine which version of ASP.NET Web Pages to use. In order to use this site, specify a version in the site’s web.config file. For more information, see the following article on the Microsoft support site: http://go.microsoft.com/fwlink/?LinkId=254126 The Web Pages registry key "{0}" does not exist. Changes were detected in the Web Pages runtime version that require your application to be recompiled. Refresh your browser window to continue. Conflicting versions of ASP.NET Web Pages detected: specified version is "{0}", but the version in bin is "{1}". To continue, remove files from the application's bin directory or remove the version specification in web.config. Specified Web Pages version "{0}" could not be found. Update your web.config to specify a different version. Current version: "{1}". ================================================ FILE: src/System.Web.WebPages.Deployment/System.Web.WebPages.Deployment.csproj ================================================  {22BABB60-8F02-4027-AFFC-ACF069954536} Library System.Web.WebPages.Deployment System.Web.WebPages.Deployment $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs Code Code True True ConfigurationResources.resx Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator ConfigurationResources.Designer.cs ================================================ FILE: src/System.Web.WebPages.Deployment/WebPagesDeployment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Web.Configuration; using System.Web.Hosting; using System.Web.WebPages.Deployment.Resources; using Microsoft.Internal.Web.Utils; using Microsoft.Win32; namespace System.Web.WebPages.Deployment { [EditorBrowsable(EditorBrowsableState.Never)] public static class WebPagesDeployment { private const string AppSettingsVersionKey = "webpages:Version"; private const string AppSettingsEnabledKey = "webpages:Enabled"; /// /// File name for a temporary file that we drop in bin to force recompilation. /// private const string ForceRecompilationFile = "WebPagesRecompilation.deleteme"; private const string WebPagesRegistryKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET Web Pages\v{0}.{1}"; internal static readonly string CacheKeyPrefix = "__System.Web.WebPages.Deployment__"; private static readonly string[] _webPagesExtensions = new[] { ".cshtml", ".vbhtml" }; private static readonly object _installPathNotFound = new object(); private static readonly IFileSystem _fileSystem = new PhysicalFileSystem(); /// Physical or virtual path to a directory where we need to determine the version of WebPages to be used. /// /// In a non-hosted scenario, this method would only look at a web.config that is present at the current path. Any config settings at an /// ancestor directory would not be considered. /// If we are unable to determine a version, we would assume that this is a v1 app. /// public static Version GetVersionWithoutEnabledCheck(string path) { return GetVersionWithoutEnabledCheckInternal(path, AssemblyUtils.WebPagesV1Version); } public static Version GetExplicitWebPagesVersion(string path) { return GetVersionWithoutEnabledCheckInternal(path, defaultVersion: null); } [Obsolete("This method is obsolete and is meant for legacy code. Use GetVersionWithoutEnabled instead.")] public static Version GetVersion(string path) { return GetObsoleteVersionInternal(path, GetAppSettings(path), new PhysicalFileSystem()); } /// /// This is meant to test an obsolete method. Don't use this! /// internal static Version GetObsoleteVersionInternal(string path, NameValueCollection configuration, IFileSystem fileSystem) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } var binDirectory = GetBinDirectory(path); var binVersion = AssemblyUtils.GetVersionFromBin(binDirectory, _fileSystem); var version = GetVersionInternal(configuration, binVersion, defaultVersion: null); if (version != null) { // If a webpages version is available in config or bin, return it. return version; } else if (AppRootContainsWebPagesFile(fileSystem, path)) { // If the path points to a WebPages site, return v1 as a fixed version. return AssemblyUtils.WebPagesV1Version; } return null; } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This operation might be expensive since it has to reflect over Assembly names.")] public static Version GetMaxVersion() { return AssemblyUtils.GetMaxWebPagesVersion(); } /// /// Determines if Asp.Net Web Pages is enabled. /// Web Pages is enabled if there's a webPages:Enabled key in AppSettings is set to "true" or if there's a cshtml file in the current path /// and the key is not present. /// /// The path at which to determine if web pages is enabled. /// /// In a non-hosted scenario, this method would only look at a web.config that is present at the current path. Any config settings at an /// ancestor directory would not be considered. /// public static bool IsEnabled(string path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } return IsEnabled(_fileSystem, path, GetAppSettings(path)); } /// /// In a non-hosted scenario, this method would only look at a web.config that is present at the current path. Any config settings at an /// ancestor directory would not be considered. /// public static bool IsExplicitlyDisabled(string path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } return IsExplicitlyDisabled(GetAppSettings(path)); } [EditorBrowsable(EditorBrowsableState.Never)] public static IDictionary GetIncompatibleDependencies(string appPath) { if (String.IsNullOrEmpty(appPath)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "appPath"); } var configFilePath = Path.Combine(appPath, "web.config"); var assemblyReferences = AppDomainHelper.GetBinAssemblyReferences(appPath, configFilePath); return AssemblyUtils.GetAssembliesMatchingOtherVersions(assemblyReferences); } internal static bool IsExplicitlyDisabled(NameValueCollection appSettings) { bool? enabled = GetEnabled(appSettings); return enabled.HasValue && enabled.Value == false; } internal static bool IsEnabled(IFileSystem fileSystem, string path, NameValueCollection appSettings) { bool? enabled = GetEnabled(appSettings); if (!enabled.HasValue) { return AppRootContainsWebPagesFile(fileSystem, path); } return enabled.Value; } /// /// Returns the value for webPages:Enabled AppSetting value in web.config. /// private static bool? GetEnabled(NameValueCollection appSettings) { string enabledSetting = appSettings.Get(AppSettingsEnabledKey); if (String.IsNullOrEmpty(enabledSetting)) { return null; } else { return Boolean.Parse(enabledSetting); } } /// /// Returns the version of WebPages to be used for a specified path. /// /// /// This method would always returns a value regardless of web pages is explicitly disabled (via config) or implicitly disabled (by virtue of not having a cshtml file) at /// the specified path. /// internal static Version GetVersionInternal(NameValueCollection appSettings, Version binVersion, Version defaultVersion) { // Return version values with the following precedence: // 1) Version in config // 2) Version in bin // 3) defaultVersion. return GetVersionFromConfig(appSettings) ?? binVersion ?? defaultVersion; } private static Version GetVersionWithoutEnabledCheckInternal(string path, Version defaultVersion) { if (String.IsNullOrEmpty(path)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path"); } var binDirectory = GetBinDirectory(path); var binVersion = AssemblyUtils.GetVersionFromBin(binDirectory, _fileSystem); return GetVersionInternal(GetAppSettings(path), binVersion, defaultVersion); } /// /// Gets full path to a folder that contains ASP.NET WebPages assemblies for a given version. Used by /// WebMatrix and Visual Studio so they know what to copy to an app's Bin folder or deploy to a hoster. /// public static string GetAssemblyPath(Version version) { if (version == null) { throw new ArgumentNullException("version"); } string webPagesRegistryKey = String.Format(CultureInfo.InvariantCulture, WebPagesRegistryKey, version.Major, version.Minor); object installPath = Registry.GetValue(webPagesRegistryKey, "InstallPath", _installPathNotFound); if (installPath == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ConfigurationResources.WebPagesRegistryKeyDoesNotExist, webPagesRegistryKey)); } else if (installPath == _installPathNotFound) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ConfigurationResources.InstallPathNotFound, webPagesRegistryKey)); } return Path.Combine((string)installPath, "Assemblies"); } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This operation might be expensive since it has to reflect over Assembly names.")] public static IEnumerable GetWebPagesAssemblies() { return AssemblyUtils.GetAssembliesForVersion(AssemblyUtils.ThisAssemblyName.Version); } private static NameValueCollection GetAppSettings(string path) { if (path.StartsWith("~/", StringComparison.Ordinal)) { // Path is virtual, assume we're hosted return (NameValueCollection)WebConfigurationManager.GetSection("appSettings", path); } else { // Path is physical, map it to an application WebConfigurationFileMap fileMap = new WebConfigurationFileMap(); fileMap.VirtualDirectories.Add("/", new VirtualDirectoryMapping(path, true)); var config = WebConfigurationManager.OpenMappedWebConfiguration(fileMap, "/"); var appSettingsSection = config.AppSettings; var appSettings = new NameValueCollection(); foreach (KeyValueConfigurationElement element in appSettingsSection.Settings) { appSettings.Add(element.Key, element.Value); } return appSettings; } } internal static Version GetVersionFromConfig(NameValueCollection appSettings) { string version = appSettings.Get(AppSettingsVersionKey); // Version will be null if the config section is registered but not present in app web.config. if (!String.IsNullOrEmpty(version)) { // Build and Revision are optional in config but required by Fusion, so we set them to 0 if unspecified in config. // Valid in config: "1.0", "1.0.0", "1.0.0.0" var fullVersion = new Version(version); if (fullVersion.Build == -1 || fullVersion.Revision == -1) { fullVersion = new Version(fullVersion.Major, fullVersion.Minor, fullVersion.Build == -1 ? 0 : fullVersion.Build, fullVersion.Revision == -1 ? 0 : fullVersion.Revision); } return fullVersion; } return null; } internal static bool AppRootContainsWebPagesFile(IFileSystem fileSystem, string path) { var files = fileSystem.EnumerateFiles(path); return files.Any(IsWebPagesFile); } private static bool IsWebPagesFile(string file) { var extension = Path.GetExtension(file); return _webPagesExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } /// /// HttpRuntime.BinDirectory is unavailable in design time and throws if we try to access it. To workaround this, if we aren't hosted, /// we will assume that the path that was passed to us is the application root. /// /// /// private static string GetBinDirectory(string path) { if (HostingEnvironment.IsHosted) { return HttpRuntime.BinDirectory; } return Path.Combine(path, "bin"); } /// /// Reads a previously persisted version number from build manager's cached directory. /// /// Null if a previous version number does not exist or is not a valid version number, read version number otherwise. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We never want to throw an exception from this method.")] internal static Version GetPreviousRuntimeVersion(IBuildManager buildManagerFileSystem) { string fileName = GetCachedFileName(); try { Stream stream = buildManagerFileSystem.ReadCachedFile(fileName); if (stream == null) { return null; } using (StreamReader reader = new StreamReader(stream)) { string text = reader.ReadLine(); Version version; if (Version.TryParse(text, out version)) { return version; } } } catch { } return null; } /// /// Persists the version number in a file under the build manager's cached directory. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We never want to throw an exception from this method.")] internal static void PersistRuntimeVersion(IBuildManager buildManager, Version version) { string fileName = GetCachedFileName(); try { Stream stream = buildManager.CreateCachedFile(fileName); using (var writer = new StreamWriter(stream)) { writer.WriteLine(version.ToString()); } } catch { } } /// /// Forces recompilation of the application by dropping a file under bin. /// /// File system instance used to write a file to bin directory. /// Path to bin directory of the application [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We never want to throw an exception from this method.")] internal static void ForceRecompile(IFileSystem fileSystem, string binDirectory) { var fileToWrite = Path.Combine(binDirectory, ForceRecompilationFile); try { // Note: We should use BuildManager::ForceRecompile once that method makes it into System.Web. using (var writer = new StreamWriter(fileSystem.OpenFile(fileToWrite))) { writer.WriteLine(); } } catch { } } /// /// Name of the the temporary file used by BuildManager.CreateCachedFile / BuildManager.ReadCachedFile where we cache WebPages's version number. /// /// private static string GetCachedFileName() { return typeof(WebPagesDeployment).Namespace; } private static string RemoveTrailingSlash(string path) { if (!String.IsNullOrEmpty(path)) { path = path.TrimEnd(Path.DirectorySeparatorChar); } return path; } } } ================================================ FILE: src/System.Web.WebPages.Deployment/packages.config ================================================  ================================================ FILE: src/System.Web.WebPages.Razor/AssemblyBuilderWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.Compilation; namespace System.Web.WebPages.Razor { internal sealed class AssemblyBuilderWrapper : IAssemblyBuilder { public AssemblyBuilderWrapper(AssemblyBuilder builder) { if (builder == null) { throw new ArgumentNullException("builder"); } InnerBuilder = builder; } internal AssemblyBuilder InnerBuilder { get; set; } public void AddCodeCompileUnit(BuildProvider buildProvider, CodeCompileUnit compileUnit) { InnerBuilder.AddCodeCompileUnit(buildProvider, compileUnit); } public void GenerateTypeFactory(string typeName) { InnerBuilder.GenerateTypeFactory(typeName); } } } ================================================ FILE: src/System.Web.WebPages.Razor/CompilingPathEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.WebPages.Razor { public class CompilingPathEventArgs : EventArgs { public CompilingPathEventArgs(string virtualPath, WebPageRazorHost host) { VirtualPath = virtualPath; Host = host; } public string VirtualPath { get; private set; } public WebPageRazorHost Host { get; set; } } } ================================================ FILE: src/System.Web.WebPages.Razor/Configuration/HostSection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Configuration; namespace System.Web.WebPages.Razor.Configuration { public class HostSection : ConfigurationSection { public static readonly string SectionName = RazorWebSectionGroup.GroupName + "/host"; private static readonly ConfigurationProperty _typeProperty = new ConfigurationProperty("factoryType", typeof(string), null, ConfigurationPropertyOptions.IsRequired); private bool _factoryTypeSet = false; private string _factoryType; [ConfigurationProperty("factoryType", IsRequired = true, DefaultValue = null)] public string FactoryType { get { return _factoryTypeSet ? _factoryType : (string)this[_typeProperty]; } set { _factoryType = value; _factoryTypeSet = true; } } } } ================================================ FILE: src/System.Web.WebPages.Razor/Configuration/RazorPagesSection.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.Web.Configuration; namespace System.Web.WebPages.Razor.Configuration { public class RazorPagesSection : ConfigurationSection { public static readonly string SectionName = RazorWebSectionGroup.GroupName + "/pages"; private static readonly ConfigurationProperty _pageBaseTypeProperty = new ConfigurationProperty("pageBaseType", typeof(string), null, ConfigurationPropertyOptions.IsRequired); private static readonly ConfigurationProperty _namespacesProperty = new ConfigurationProperty("namespaces", typeof(NamespaceCollection), null, ConfigurationPropertyOptions.IsRequired); private bool _pageBaseTypeSet = false; private bool _namespacesSet = false; private string _pageBaseType; private NamespaceCollection _namespaces; [ConfigurationProperty("pageBaseType", IsRequired = true)] public string PageBaseType { get { return _pageBaseTypeSet ? _pageBaseType : (string)this[_pageBaseTypeProperty]; } set { _pageBaseType = value; _pageBaseTypeSet = true; } } [ConfigurationProperty("namespaces", IsRequired = true)] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Being able to set this property is extremely useful for third-parties who are testing components which interact with the Razor configuration system")] public NamespaceCollection Namespaces { get { return _namespacesSet ? _namespaces : (NamespaceCollection)this[_namespacesProperty]; } set { _namespaces = value; _namespacesSet = true; } } } } ================================================ FILE: src/System.Web.WebPages.Razor/Configuration/RazorWebSectionGroup.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Configuration; namespace System.Web.WebPages.Razor.Configuration { public class RazorWebSectionGroup : ConfigurationSectionGroup { public static readonly string GroupName = "system.web.webPages.razor"; // Use flags instead of null values since tests may want to set the property to null private bool _hostSet = false; private bool _pagesSet = false; private HostSection _host; private RazorPagesSection _pages; [ConfigurationProperty("host", IsRequired = false)] public HostSection Host { get { return _hostSet ? _host : (HostSection)Sections["host"]; } set { _host = value; _hostSet = true; } } [ConfigurationProperty("pages", IsRequired = false)] public RazorPagesSection Pages { get { return _pagesSet ? _pages : (RazorPagesSection)Sections["pages"]; } set { _pages = value; _pagesSet = true; } } } } ================================================ FILE: src/System.Web.WebPages.Razor/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. ================================================ FILE: src/System.Web.WebPages.Razor/HostingEnvironmentWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Hosting; namespace System.Web.WebPages.Razor { internal sealed class HostingEnvironmentWrapper : IHostingEnvironment { public string MapPath(string virtualPath) { return HostingEnvironment.MapPath(virtualPath); } } } ================================================ FILE: src/System.Web.WebPages.Razor/IAssemblyBuilder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Web.Compilation; namespace System.Web.WebPages.Razor { internal interface IAssemblyBuilder { void AddCodeCompileUnit(BuildProvider buildProvider, CodeCompileUnit compileUnit); void GenerateTypeFactory(string typeName); } } ================================================ FILE: src/System.Web.WebPages.Razor/IHostingEnvironment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Web.WebPages.Razor { internal interface IHostingEnvironment { string MapPath(string virtualPath); } } ================================================ FILE: src/System.Web.WebPages.Razor/PreApplicationStartCode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Web.Compilation; namespace System.Web.WebPages.Razor { [EditorBrowsable(EditorBrowsableState.Never)] public static class PreApplicationStartCode { // NOTE: Do not add public fields, methods, or other members to this class. // This class does not show up in Intellisense so members on it will not be // discoverable by users. Place new members on more appropriate classes that // relate to the public API (for example, a LoginUrl property should go on a // membership-related class). private static bool _startWasCalled; public static void Start() { // Even though ASP.NET will only call each PreAppStart once, we sometimes internally call one PreAppStart from // another PreAppStart to ensure that things get initialized in the right order. ASP.NET does not guarantee the // order so we have to guard against multiple calls. // All Start calls are made on same thread, so no lock needed here. if (_startWasCalled) { return; } _startWasCalled = true; BuildProvider.RegisterBuildProvider(".cshtml", typeof(RazorBuildProvider)); BuildProvider.RegisterBuildProvider(".vbhtml", typeof(RazorBuildProvider)); } } } ================================================ FILE: src/System.Web.WebPages.Razor/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using System.Web.WebPages.Razor; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("System.Web.WebPages.Razor")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("System.Web.WebPages.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] ================================================ FILE: src/System.Web.WebPages.Razor/RazorBuildProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Security; using System.Web.Compilation; using System.Web.Razor; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; namespace System.Web.WebPages.Razor { [BuildProviderAppliesTo(BuildProviderAppliesTo.Web | BuildProviderAppliesTo.Code)] public class RazorBuildProvider : BuildProvider { private static bool? _isFullTrust; private CodeCompileUnit _generatedCode = null; private WebPageRazorHost _host = null; private IList _virtualPathDependencies; private IAssemblyBuilder _assemblyBuilder; public static event EventHandler CodeGenerationCompleted; internal event EventHandler CodeGenerationCompletedInternal { add { _codeGenerationCompletedInternal += value; } remove { _codeGenerationCompletedInternal -= value; } } public static event EventHandler CodeGenerationStarted; /// /// For unit testing. /// internal event EventHandler CodeGenerationStartedInternal { add { _codeGenerationStartedInternal += value; } remove { _codeGenerationStartedInternal -= value; } } public static event EventHandler CompilingPath; /// /// For unit testing /// private event EventHandler _codeGenerationCompletedInternal; private event EventHandler _codeGenerationStartedInternal; internal WebPageRazorHost Host { get { if (_host == null) { _host = CreateHost(); } return _host; } set { _host = value; } } // Returns the base dependencies and any dependencies added via AddVirtualPathDependencies public override ICollection VirtualPathDependencies { get { if (_virtualPathDependencies != null) { // Return a readonly wrapper so as to prevent users from modifying the collection directly. return ArrayList.ReadOnly(_virtualPathDependencies); } else { return base.VirtualPathDependencies; } } } public new string VirtualPath { get { return base.VirtualPath; } } public AssemblyBuilder AssemblyBuilder { get { var wrapper = _assemblyBuilder as AssemblyBuilderWrapper; if (wrapper != null) { return wrapper.InnerBuilder; } else { return null; } } } // For unit testing internal IAssemblyBuilder AssemblyBuilderInternal { get { return _assemblyBuilder; } } internal CodeCompileUnit GeneratedCode { get { EnsureGeneratedCode(); return _generatedCode; } set { _generatedCode = value; } } public override CompilerType CodeCompilerType { get { EnsureGeneratedCode(); CompilerType compilerType = GetDefaultCompilerTypeForLanguage(Host.CodeLanguage.LanguageName); if (_isFullTrust != false && Host.DefaultDebugCompilation) { try { SetIncludeDebugInfoFlag(compilerType); _isFullTrust = true; } catch (SecurityException) { _isFullTrust = false; } } return compilerType; } } public void AddVirtualPathDependency(string dependency) { if (_virtualPathDependencies == null) { // Initialize the collection containing the base dependencies _virtualPathDependencies = new ArrayList(base.VirtualPathDependencies); } _virtualPathDependencies.Add(dependency); } public override Type GetGeneratedType(CompilerResults results) { return results.CompiledAssembly.GetType(String.Format(CultureInfo.CurrentCulture, "{0}.{1}", Host.DefaultNamespace, Host.DefaultClassName)); } public override void GenerateCode(AssemblyBuilder assemblyBuilder) { GenerateCodeCore(new AssemblyBuilderWrapper(assemblyBuilder)); } internal virtual void GenerateCodeCore(IAssemblyBuilder assemblyBuilder) { OnCodeGenerationStarted(assemblyBuilder); assemblyBuilder.AddCodeCompileUnit(this, GeneratedCode); assemblyBuilder.GenerateTypeFactory(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", Host.DefaultNamespace, Host.DefaultClassName)); } protected internal virtual TextReader InternalOpenReader() { return OpenReader(); } protected internal virtual WebPageRazorHost CreateHost() { // Get the host from config WebPageRazorHost configuredHost = GetHostFromConfig(); // Fire the event CompilingPathEventArgs args = new CompilingPathEventArgs(VirtualPath, configuredHost); OnBeforeCompilePath(args); // Return the host provided in the args, which may have been changed by the handler return args.Host; } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method performs significant work and a property would not be appropriate")] protected internal virtual WebPageRazorHost GetHostFromConfig() { return WebRazorHostFactory.CreateHostFromConfig(VirtualPath); } protected virtual void OnBeforeCompilePath(CompilingPathEventArgs args) { EventHandler handler = CompilingPath; if (handler != null) { handler(this, args); } } private void OnCodeGenerationStarted(IAssemblyBuilder assemblyBuilder) { _assemblyBuilder = assemblyBuilder; EventHandler handler = _codeGenerationStartedInternal ?? CodeGenerationStarted; if (handler != null) { handler(this, null); } } private void OnCodeGenerationCompleted(CodeCompileUnit generatedCode) { EventHandler handler = _codeGenerationCompletedInternal ?? CodeGenerationCompleted; if (handler != null) { handler(this, new CodeGenerationCompleteEventArgs(Host.VirtualPath, Host.PhysicalPath, generatedCode)); } } private void EnsureGeneratedCode() { if (_generatedCode == null) { RazorTemplateEngine engine = new RazorTemplateEngine(Host); GeneratorResults results = null; using (TextReader reader = InternalOpenReader()) { results = engine.GenerateCode(reader, className: null, rootNamespace: null, sourceFileName: Host.PhysicalPath); } if (!results.Success) { throw CreateExceptionFromParserError(results.ParserErrors.Last(), VirtualPath); } _generatedCode = results.GeneratedCode; // Run the code gen complete event OnCodeGenerationCompleted(_generatedCode); } } private static HttpParseException CreateExceptionFromParserError(RazorError error, string virtualPath) { return new HttpParseException(error.Message + Environment.NewLine, null, virtualPath, null, error.Location.LineIndex + 1); } [SuppressMessage("Microsoft.Security", "CA2141:TransparentMethodsMustNotSatisfyLinkDemandsFxCopRule", Justification = "We are catching the SecurityException to detect medium trust")] private static void SetIncludeDebugInfoFlag(CompilerType compilerType) { compilerType.CompilerParameters.IncludeDebugInformation = true; } } } ================================================ FILE: src/System.Web.WebPages.Razor/Resources/RazorWebResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace System.Web.WebPages.Razor.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class RazorWebResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal RazorWebResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Web.WebPages.Razor.Resources.RazorWebResources", typeof(RazorWebResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Could not determine the code language for "{0}".. /// internal static string BuildProvider_No_CodeLanguageService_For_Path { get { return ResourceManager.GetString("BuildProvider_No_CodeLanguageService_For_Path", resourceCulture); } } /// /// Looks up a localized string similar to Could not locate Razor Host Factory type: {0}. /// internal static string Could_Not_Locate_FactoryType { get { return ResourceManager.GetString("Could_Not_Locate_FactoryType", resourceCulture); } } } } ================================================ FILE: src/System.Web.WebPages.Razor/Resources/RazorWebResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Could not determine the code language for "{0}". Could not locate Razor Host Factory type: {0} ================================================ FILE: src/System.Web.WebPages.Razor/System.Web.WebPages.Razor.csproj ================================================  {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} Library System.Web.WebPages.Razor System.Web.WebPages.Razor $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True RazorWebResources.resx {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {8F18041B-9410-4C36-A9C5-067813DF5F31} System.Web.Razor Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator RazorWebResources.Designer.cs ================================================ FILE: src/System.Web.WebPages.Razor/WebCodeRazorHost.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Web.Razor.Generator; using System.Web.Razor.Parser; namespace System.Web.WebPages.Razor { public class WebCodeRazorHost : WebPageRazorHost { private const string AppCodeDir = "App_Code"; private const string HttpContextAccessorName = "Context"; private static readonly string _helperPageBaseType = typeof(HelperPage).FullName; [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The code path is safe, it is a property setter and not dependent on other state")] public WebCodeRazorHost(string virtualPath) : base(virtualPath) { DefaultBaseClass = _helperPageBaseType; DefaultNamespace = DetermineNamespace(virtualPath); DefaultDebugCompilation = false; StaticHelpers = true; } [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The code path is safe, it is a property setter and not dependent on other state")] public WebCodeRazorHost(string virtualPath, string physicalPath) : base(virtualPath, physicalPath) { DefaultBaseClass = _helperPageBaseType; DefaultNamespace = DetermineNamespace(virtualPath); DefaultDebugCompilation = false; StaticHelpers = true; } public override void PostProcessGeneratedCode(CodeGeneratorContext context) { base.PostProcessGeneratedCode(context); // Yank out the execute method (ignored in Razor Web Code pages) context.GeneratedClass.Members.Remove(context.TargetMethod); // Make ApplicationInstance static CodeMemberProperty appInstanceProperty = context.GeneratedClass.Members .OfType() .Where(p => ApplicationInstancePropertyName .Equals(p.Name)) .SingleOrDefault(); if (appInstanceProperty != null) { appInstanceProperty.Attributes |= MemberAttributes.Static; } } protected override string GetClassName(string virtualPath) { return ParserHelpers.SanitizeClassName(Path.GetFileNameWithoutExtension(virtualPath)); } private static string DetermineNamespace(string virtualPath) { // Normailzize the virtual path virtualPath = virtualPath.Replace(Path.DirectorySeparatorChar, '/'); // Get the directory virtualPath = GetDirectory(virtualPath); // Skip the App_Code segment if any int appCodeIndex = virtualPath.IndexOf(AppCodeDir, StringComparison.OrdinalIgnoreCase); if (appCodeIndex != -1) { virtualPath = virtualPath.Substring(appCodeIndex + AppCodeDir.Length); } // Get the segments removing any empty entries IEnumerable segments = virtualPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (!segments.Any()) { return WebDefaultNamespace; } return WebDefaultNamespace + "." + String.Join(".", segments); } private static string GetDirectory(string virtualPath) { int lastSlash = virtualPath.LastIndexOf('/'); if (lastSlash != -1) { return virtualPath.Substring(0, lastSlash); } return String.Empty; } } } ================================================ FILE: src/System.Web.WebPages.Razor/WebPageRazorHost.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.CodeDom; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Web.Compilation; using System.Web.Hosting; using System.Web.Razor; using System.Web.Razor.Generator; using System.Web.Razor.Parser; using System.Web.WebPages.Instrumentation; using System.Web.WebPages.Razor.Resources; using Microsoft.Internal.Web.Utils; namespace System.Web.WebPages.Razor { public class WebPageRazorHost : RazorEngineHost { // DevDiv Bug 941404 - Add a prefix and folder name to class names internal const string PageClassNamePrefix = "_Page_"; internal const string ApplicationInstancePropertyName = "ApplicationInstance"; internal const string ContextPropertyName = "Context"; internal const string DefineSectionMethodName = "DefineSection"; internal const string WebDefaultNamespace = "ASP"; internal const string WriteToMethodName = "WriteTo"; internal const string WriteLiteralToMethodName = "WriteLiteralTo"; internal const string BeginContextMethodName = "BeginContext"; internal const string EndContextMethodName = "EndContext"; internal const string ResolveUrlMethodName = "Href"; private const string ApplicationStartFileName = "_AppStart"; private const string PageStartFileName = "_PageStart"; internal static readonly string FallbackApplicationTypeName = typeof(HttpApplication).FullName; internal static readonly string PageBaseClass = typeof(WebPage).FullName; internal static readonly string TemplateTypeName = typeof(HelperResult).FullName; private static ConcurrentDictionary _importedNamespaces = new ConcurrentDictionary(); private readonly Dictionary _specialFileBaseTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); private string _className; private RazorCodeLanguage _codeLanguage; private string _globalAsaxTypeName; private bool? _isSpecialPage; private string _physicalPath = null; private string _specialFileBaseClass; [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The code path is safe, it is a property setter and not dependent on other state")] private WebPageRazorHost() { NamespaceImports.Add("System"); NamespaceImports.Add("System.Collections.Generic"); NamespaceImports.Add("System.IO"); NamespaceImports.Add("System.Linq"); NamespaceImports.Add("System.Net"); NamespaceImports.Add("System.Web"); NamespaceImports.Add("System.Web.Helpers"); NamespaceImports.Add("System.Web.Security"); NamespaceImports.Add("System.Web.UI"); NamespaceImports.Add("System.Web.WebPages"); NamespaceImports.Add("System.Web.WebPages.Html"); RegisterSpecialFile(ApplicationStartFileName, typeof(ApplicationStartPage)); RegisterSpecialFile(PageStartFileName, typeof(StartPage)); DefaultNamespace = WebDefaultNamespace; GeneratedClassContext = new GeneratedClassContext(GeneratedClassContext.DefaultExecuteMethodName, GeneratedClassContext.DefaultWriteMethodName, GeneratedClassContext.DefaultWriteLiteralMethodName, WriteToMethodName, WriteLiteralToMethodName, TemplateTypeName, DefineSectionMethodName, BeginContextMethodName, EndContextMethodName) { ResolveUrlMethodName = ResolveUrlMethodName }; DefaultPageBaseClass = PageBaseClass; DefaultDebugCompilation = true; EnableInstrumentation = false; } public WebPageRazorHost(string virtualPath) : this(virtualPath, null) { } [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The code path is safe, it is a property setter and not dependent on other state")] public WebPageRazorHost(string virtualPath, string physicalPath) : this() { if (String.IsNullOrEmpty(virtualPath)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "virtualPath"), "virtualPath"); } VirtualPath = virtualPath; PhysicalPath = physicalPath; DefaultClassName = GetClassName(VirtualPath); CodeLanguage = GetCodeLanguage(); EnableInstrumentation = new InstrumentationService().IsAvailable; } public override RazorCodeLanguage CodeLanguage { get { if (_codeLanguage == null) { _codeLanguage = GetCodeLanguage(); } return _codeLanguage; } protected set { _codeLanguage = value; } } public override string DefaultBaseClass { get { if (base.DefaultBaseClass != null) { return base.DefaultBaseClass; } if (IsSpecialPage) { return SpecialPageBaseClass; } else { return DefaultPageBaseClass; } } set { base.DefaultBaseClass = value; } } public override string DefaultClassName { get { if (_className == null) { _className = GetClassName(VirtualPath); } return _className; } set { _className = value; } } public bool DefaultDebugCompilation { get; set; } public string DefaultPageBaseClass { get; set; } internal string GlobalAsaxTypeName { get { return _globalAsaxTypeName ?? (HostingEnvironment.IsHosted ? BuildManager.GetGlobalAsaxType().FullName : FallbackApplicationTypeName); } set { _globalAsaxTypeName = value; } } public bool IsSpecialPage { get { CheckForSpecialPage(); return _isSpecialPage.Value; } } public string PhysicalPath { get { MapPhysicalPath(); return _physicalPath; } set { _physicalPath = value; } } public override string InstrumentedSourceFilePath { get { return VirtualPath; } set { VirtualPath = value; } } private string SpecialPageBaseClass { get { CheckForSpecialPage(); return _specialFileBaseClass; } } public string VirtualPath { get; private set; } /// /// Adds a namespace to the global imports list for this AppDomain /// /// /// IMPORTANT: ALL uses of WebPageRazorHost (and derived classes) within the same AppDomain will share this list. /// Therefore this method should only be used in runtime scenarios, not in design-time scenarios where the Razor /// data structures for multiple files and projects may be shared within a single AppDomain. /// /// The namespace to add to the global imports list. public static void AddGlobalImport(string ns) { if (String.IsNullOrEmpty(ns)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "ns"), "ns"); } _importedNamespaces.TryAdd(ns, null); } private void CheckForSpecialPage() { if (!_isSpecialPage.HasValue) { string fileName = Path.GetFileNameWithoutExtension(VirtualPath); string baseType; if (_specialFileBaseTypes.TryGetValue(fileName, out baseType)) { _isSpecialPage = true; _specialFileBaseClass = baseType; } else { _isSpecialPage = false; } } } public override ParserBase CreateMarkupParser() { return new HtmlMarkupParser(); } private static RazorCodeLanguage DetermineCodeLanguage(string fileName) { string extension = Path.GetExtension(fileName); // Use an if rather than else-if just in case Path.GetExtension returns null for some reason if (String.IsNullOrEmpty(extension)) { return null; } if (extension[0] == '.') { extension = extension.Substring(1); // Trim off the dot } // Look up the language // At the moment this only deals with code languages: cs, vb, etc., but in theory we could have MarkupLanguageServices which allow for // interesting combinations like: vbcss, csxml, etc. RazorCodeLanguage language = GetLanguageByExtension(extension); return language; } protected virtual string GetClassName(string virtualPath) { // Remove "~/" and run through our santizer // For example, for ~/Foo/Bar/Baz.cshtml, the class name is _Page_Foo_Bar_Baz_cshtml return ParserHelpers.SanitizeClassName(PageClassNamePrefix + virtualPath.TrimStart('~', '/')); } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Method involves significant processing and should not be a property")] protected virtual RazorCodeLanguage GetCodeLanguage() { RazorCodeLanguage language = DetermineCodeLanguage(VirtualPath); if (language == null && !String.IsNullOrEmpty(PhysicalPath)) { language = DetermineCodeLanguage(PhysicalPath); } if (language == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, RazorWebResources.BuildProvider_No_CodeLanguageService_For_Path, VirtualPath)); } return language; } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This method involves copying memory, so a property is not appropriate")] public static IEnumerable GetGlobalImports() { return _importedNamespaces.ToArray().Select(pair => pair.Key); } private static RazorCodeLanguage GetLanguageByExtension(string extension) { return RazorCodeLanguage.GetLanguageByExtension(extension); } private void MapPhysicalPath() { if (_physicalPath == null && HostingEnvironment.IsHosted) { string path = HostingEnvironment.MapPath(VirtualPath); if (!String.IsNullOrEmpty(path) && File.Exists(path)) { _physicalPath = path; } } } public override void PostProcessGeneratedCode(CodeGeneratorContext context) { base.PostProcessGeneratedCode(context); // Add additional global imports context.Namespace.Imports.AddRange(GetGlobalImports().Select(s => new CodeNamespaceImport(s)).ToArray()); // Create ApplicationInstance property CodeMemberProperty prop = new CodeMemberProperty() { Name = ApplicationInstancePropertyName, Type = new CodeTypeReference(GlobalAsaxTypeName), HasGet = true, HasSet = false, Attributes = MemberAttributes.Family | MemberAttributes.Final }; prop.GetStatements.Add( new CodeMethodReturnStatement( new CodeCastExpression( new CodeTypeReference(GlobalAsaxTypeName), new CodePropertyReferenceExpression( new CodePropertyReferenceExpression( null, ContextPropertyName), ApplicationInstancePropertyName)))); context.GeneratedClass.Members.Insert(0, prop); } protected void RegisterSpecialFile(string fileName, Type baseType) { if (baseType == null) { throw new ArgumentNullException("baseType"); } RegisterSpecialFile(fileName, baseType.FullName); } protected void RegisterSpecialFile(string fileName, string baseTypeName) { if (String.IsNullOrEmpty(fileName)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "fileName"), "fileName"); } if (String.IsNullOrEmpty(baseTypeName)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "baseTypeName"), "baseTypeName"); } _specialFileBaseTypes[fileName] = baseTypeName; } } } ================================================ FILE: src/System.Web.WebPages.Razor/WebRazorHostFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Web.Compilation; using System.Web.Configuration; using System.Web.Hosting; using System.Web.WebPages.Razor.Configuration; using System.Web.WebPages.Razor.Resources; using Microsoft.Internal.Web.Utils; namespace System.Web.WebPages.Razor { public class WebRazorHostFactory { private static ConcurrentDictionary> _factories = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); internal static Func TypeFactory = DefaultTypeFactory; public static WebPageRazorHost CreateDefaultHost(string virtualPath) { return CreateDefaultHost(virtualPath, null); } public static WebPageRazorHost CreateDefaultHost(string virtualPath, string physicalPath) { return CreateHostFromConfigCore(null, virtualPath, physicalPath); } public static WebPageRazorHost CreateHostFromConfig(string virtualPath) { return CreateHostFromConfig(virtualPath, null); } public static WebPageRazorHost CreateHostFromConfig(string virtualPath, string physicalPath) { if (String.IsNullOrEmpty(virtualPath)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "virtualPath"), "virtualPath"); } return CreateHostFromConfigCore(GetRazorSection(virtualPath), virtualPath, physicalPath); } public static WebPageRazorHost CreateHostFromConfig(RazorWebSectionGroup config, string virtualPath) { return CreateHostFromConfig(config, virtualPath, null); } public static WebPageRazorHost CreateHostFromConfig(RazorWebSectionGroup config, string virtualPath, string physicalPath) { if (config == null) { throw new ArgumentNullException("config"); } if (String.IsNullOrEmpty(virtualPath)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "virtualPath"), "virtualPath"); } return CreateHostFromConfigCore(config, virtualPath, physicalPath); } internal static WebPageRazorHost CreateHostFromConfigCore(RazorWebSectionGroup config, string virtualPath, string physicalPath) { // Use the virtual path to select a host environment for the generated code // Do this check here because the App_Code host can't be overridden. // Make the path app relative virtualPath = EnsureAppRelative(virtualPath); WebPageRazorHost host; if (virtualPath.StartsWith("~/App_Code", StringComparison.OrdinalIgnoreCase)) { // Under App_Code => It's a Web Code file host = new WebCodeRazorHost(virtualPath, physicalPath); } else { WebRazorHostFactory factory = null; if (config != null && config.Host != null && !String.IsNullOrEmpty(config.Host.FactoryType)) { Func factoryCreator = _factories.GetOrAdd(config.Host.FactoryType, CreateFactory); Debug.Assert(factoryCreator != null); // CreateFactory should throw if there's an error creating the factory factory = factoryCreator(); } host = (factory ?? new WebRazorHostFactory()).CreateHost(virtualPath, physicalPath); if (config != null && config.Pages != null) { ApplyConfigurationToHost(config.Pages, host); } } return host; } private static Func CreateFactory(string typeName) { Type factoryType = TypeFactory(typeName); if (factoryType == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, RazorWebResources.Could_Not_Locate_FactoryType, typeName)); } return Expression.Lambda>(Expression.New(factoryType)) .Compile(); } public static void ApplyConfigurationToHost(RazorPagesSection config, WebPageRazorHost host) { host.DefaultPageBaseClass = config.PageBaseType; // Add imports foreach (string import in config.Namespaces.OfType().Select(ns => ns.Namespace)) { host.NamespaceImports.Add(import); } } public virtual WebPageRazorHost CreateHost(string virtualPath, string physicalPath) { return new WebPageRazorHost(virtualPath, physicalPath); } internal static RazorWebSectionGroup GetRazorSection(string virtualPath) { // Get the individual sections (we can only use GetSection in medium trust) and then reconstruct the section group return new RazorWebSectionGroup() { Host = (HostSection)WebConfigurationManager.GetSection(HostSection.SectionName, virtualPath), Pages = (RazorPagesSection)WebConfigurationManager.GetSection(RazorPagesSection.SectionName, virtualPath) }; } #if CODE_COVERAGE [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] // JUSTIFICATION: VirtualPathUtility.ToAppRelative is only available in ASP.Net environment #endif private static string EnsureAppRelative(string virtualPath) { if (HostingEnvironment.IsHosted) { virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); } else { if (virtualPath.StartsWith("/", StringComparison.Ordinal)) { virtualPath = "~" + virtualPath; } else if (!virtualPath.StartsWith("~/", StringComparison.Ordinal)) { virtualPath = "~/" + virtualPath; } } return virtualPath; } #if CODE_COVERAGE [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] // JUSTIFICATION: BuildManager.GetType is only available in ASP.Net environment #endif private static Type DefaultTypeFactory(string typeName) { return BuildManager.GetType(typeName, false, false); } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ApiDescriptionExtensions.cs ================================================ using System; using System.Text; using System.Web; using System.Web.Http.Description; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { public static class ApiDescriptionExtensions { /// /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" /// /// The . /// The ID as a string. public static string GetFriendlyId(this ApiDescription description) { string path = description.RelativePath; string[] urlParts = path.Split('?'); string localPath = urlParts[0]; string queryKeyString = null; if (urlParts.Length > 1) { string query = urlParts[1]; string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys; queryKeyString = String.Join("_", queryKeys); } StringBuilder friendlyPath = new StringBuilder(); friendlyPath.AppendFormat("{0}-{1}", description.HttpMethod.Method, localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)); if (queryKeyString != null) { friendlyPath.AppendFormat("_{0}", queryKeyString.Replace('.', '-')); } return friendlyPath.ToString(); } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/App_Start/HelpPageConfig.cs ================================================ // Uncomment the following to provide samples for PageResult. Must also add the Microsoft.AspNet.WebApi.OData // package to your project. ////#define Handle_PageResultOfT using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Web; using System.Web.Http; #if Handle_PageResultOfT using System.Web.Http.OData; #endif namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// Use this class to customize the Help Page. /// For example you can set a custom to supply the documentation /// or you can provide the samples for the requests/responses. /// public static class HelpPageConfig { [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.TextSample.#ctor(System.String)", Justification = "End users may choose to merge this string with existing localized resources.")] [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "bsonspec", Justification = "Part of a URI.")] public static void Register(HttpConfiguration config) { //// Uncomment the following to use the documentation from XML documentation file. //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))); //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. //// Also, the string arrays will be used for IEnumerable. The sample objects will be serialized into different media type //// formats by the available formatters. //config.SetSampleObjects(new Dictionary //{ // {typeof(string), "sample string"}, // {typeof(IEnumerable), new string[]{"sample 1", "sample 2"}} //}); // Extend the following to provide factories for types not handled automatically (those lacking parameterless // constructors) or for which you prefer to use non-default property values. Line below provides a fallback // since automatic handling will fail and GeneratePageResult handles only a single type. #if Handle_PageResultOfT config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult); #endif // Extend the following to use a preset object directly as the sample for all actions that support a media // type, regardless of the body parameter or return type. The lines below avoid display of binary content. // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. config.SetSampleForMediaType( new TextSample("Binary JSON content. See http://bsonspec.org for details."), new MediaTypeHeaderValue("application/bson")); //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format //// and have IEnumerable as the body parameter or return type. //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable)); //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" //// and action named "Put". //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put"); //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" //// on the controller named "Values" and action named "Get" with parameter "id". //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id"); //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent. //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter. //config.SetActualRequestType(typeof(string), "Values", "Get"); //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent. //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string. //config.SetActualResponseType(typeof(string), "Values", "Post"); } #if Handle_PageResultOfT private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type) { if (type.IsGenericType) { Type openGenericType = type.GetGenericTypeDefinition(); if (openGenericType == typeof(PageResult<>)) { // Get the T in PageResult Type[] typeParameters = type.GetGenericArguments(); Debug.Assert(typeParameters.Length == 1); // Create an enumeration to pass as the first parameter to the PageResult constuctor Type itemsType = typeof(List<>).MakeGenericType(typeParameters); object items = sampleGenerator.GetSampleObject(itemsType); // Fill in the other information needed to invoke the PageResult constuctor Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), }; object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, }; // Call PageResult(IEnumerable items, Uri nextPageLink, long? count) constructor ConstructorInfo constructor = type.GetConstructor(parameterTypes); return constructor.Invoke(parameters); } } return null; } #endif } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Controllers/HelpController.cs ================================================ using System; using System.Web.Http; using System.Web.Mvc; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers { /// /// The controller that will handle requests for the help page. /// public class HelpController : Controller { private const string ErrorViewName = "Error"; public HelpController() : this(GlobalConfiguration.Configuration) { } public HelpController(HttpConfiguration config) { Configuration = config; } public HttpConfiguration Configuration { get; private set; } public ActionResult Index() { ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider(); return View(Configuration.Services.GetApiExplorer().ApiDescriptions); } public ActionResult Api(string apiId) { if (!String.IsNullOrEmpty(apiId)) { HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); if (apiModel != null) { return View(apiModel); } } return View(ErrorViewName); } public ActionResult ResourceModel(string modelName) { if (!String.IsNullOrEmpty(modelName)) { ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator(); ModelDescription modelDescription; if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription)) { return View(modelDescription); } } return View(ErrorViewName); } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/HelpPage.css ================================================ .help-page h1, .help-page .h1, .help-page h2, .help-page .h2, .help-page h3, .help-page .h3, #body.help-page, .help-page-table th, .help-page-table pre, .help-page-table p { font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; } .help-page pre.wrapped { white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; } .help-page .warning-message-container { margin-top: 20px; padding: 0 10px; color: #525252; background: #EFDCA9; border: 1px solid #CCCCCC; } .help-page-table { width: 100%; border-collapse: collapse; text-align: left; margin: 0px 0px 20px 0px; border-top: 1px solid #D4D4D4; } .help-page-table th { text-align: left; font-weight: bold; border-bottom: 1px solid #D4D4D4; padding: 5px 6px 5px 6px; } .help-page-table td { border-bottom: 1px solid #D4D4D4; padding: 10px 8px 10px 8px; vertical-align: top; } .help-page-table pre, .help-page-table p { margin: 0px; padding: 0px; font-family: inherit; font-size: 100%; } .help-page-table tbody tr:hover td { background-color: #F3F3F3; } .help-page a:hover { background-color: transparent; } .help-page .sample-header { border: 2px solid #D4D4D4; background: #00497E; color: #FFFFFF; padding: 8px 15px; border-bottom: none; display: inline-block; margin: 10px 0px 0px 0px; } .help-page .sample-content { display: block; border-width: 0; padding: 15px 20px; background: #FFFFFF; border: 2px solid #D4D4D4; margin: 0px 0px 10px 0px; } .help-page .api-name { width: 40%; } .help-page .api-documentation { width: 60%; } .help-page .parameter-name { width: 20%; } .help-page .parameter-documentation { width: 40%; } .help-page .parameter-type { width: 20%; } .help-page .parameter-annotations { width: 20%; } .help-page h1, .help-page .h1 { font-size: 36px; line-height: normal; } .help-page h2, .help-page .h2 { font-size: 24px; } .help-page h3, .help-page .h3 { font-size: 20px; } #body.help-page { font-size: 14px; line-height: 143%; color: #333; } .help-page a { color: #0000EE; text-decoration: none; } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/HelpPageAreaRegistration.cs ================================================ using System.Web.Http; using System.Web.Mvc; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { public class HelpPageAreaRegistration : AreaRegistration { public override string AreaName { get { return "HelpPage"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "HelpPage_Default", "Help/{action}/{apiId}", new { controller = "Help", action = "Index", apiId = UrlParameter.Optional }); HelpPageConfig.Register(GlobalConfiguration.Configuration); } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/HelpPageConfigurationExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Description; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { public static class HelpPageConfigurationExtensions { private const string ApiModelPrefix = "MS_HelpPageApiModel_"; /// /// Sets the documentation provider for help page. /// /// The . /// The documentation provider. public static void SetDocumentationProvider(this HttpConfiguration config, IDocumentationProvider documentationProvider) { config.Services.Replace(typeof(IDocumentationProvider), documentationProvider); } /// /// Sets the objects that will be used by the formatters to produce sample requests/responses. /// /// The . /// The sample objects. public static void SetSampleObjects(this HttpConfiguration config, IDictionary sampleObjects) { config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects; } /// /// Sets the sample request directly for the specified media type and action. /// /// The . /// The sample request. /// The media type. /// Name of the controller. /// Name of the action. public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, new[] { "*" }), sample); } /// /// Sets the sample request directly for the specified media type and action with parameters. /// /// The . /// The sample request. /// The media type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetSampleRequest(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample); } /// /// Sets the sample request directly for the specified media type of the action. /// /// The . /// The sample response. /// The media type. /// Name of the controller. /// Name of the action. public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, new[] { "*" }), sample); } /// /// Sets the sample response directly for the specified media type of the action with specific parameters. /// /// The . /// The sample response. /// The media type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetSampleResponse(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample); } /// /// Sets the sample directly for all actions with the specified media type. /// /// The . /// The sample. /// The media type. public static void SetSampleForMediaType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType), sample); } /// /// Sets the sample directly for all actions with the specified type and media type. /// /// The . /// The sample. /// The media type. /// The parameter type or return type of an action. public static void SetSampleForType(this HttpConfiguration config, object sample, MediaTypeHeaderValue mediaType, Type type) { config.GetHelpPageSampleGenerator().ActionSamples.Add(new HelpPageSampleKey(mediaType, type), sample); } /// /// Specifies the actual type of passed to the in an action. /// The help page will use this information to produce more accurate request samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, new[] { "*" }), type); } /// /// Specifies the actual type of passed to the in an action. /// The help page will use this information to produce more accurate request samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetActualRequestType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type); } /// /// Specifies the actual type of returned as part of the in an action. /// The help page will use this information to produce more accurate response samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, new[] { "*" }), type); } /// /// Specifies the actual type of returned as part of the in an action. /// The help page will use this information to produce more accurate response samples. /// /// The . /// The type. /// Name of the controller. /// Name of the action. /// The parameter names. public static void SetActualResponseType(this HttpConfiguration config, Type type, string controllerName, string actionName, params string[] parameterNames) { config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(new HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type); } /// /// Gets the help page sample generator. /// /// The . /// The help page sample generator. public static HelpPageSampleGenerator GetHelpPageSampleGenerator(this HttpConfiguration config) { return (HelpPageSampleGenerator)config.Properties.GetOrAdd( typeof(HelpPageSampleGenerator), k => new HelpPageSampleGenerator()); } /// /// Sets the help page sample generator. /// /// The . /// The help page sample generator. public static void SetHelpPageSampleGenerator(this HttpConfiguration config, HelpPageSampleGenerator sampleGenerator) { config.Properties.AddOrUpdate( typeof(HelpPageSampleGenerator), k => sampleGenerator, (k, o) => sampleGenerator); } /// /// Gets the model description generator. /// /// The configuration. /// The public static ModelDescriptionGenerator GetModelDescriptionGenerator(this HttpConfiguration config) { return (ModelDescriptionGenerator)config.Properties.GetOrAdd( typeof(ModelDescriptionGenerator), k => InitializeModelDescriptionGenerator(config)); } /// /// Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls. /// /// The . /// The ID. /// /// An /// public static HelpPageApiModel GetHelpPageApiModel(this HttpConfiguration config, string apiDescriptionId) { object model; string modelId = ApiModelPrefix + apiDescriptionId; if (!config.Properties.TryGetValue(modelId, out model)) { Collection apiDescriptions = config.Services.GetApiExplorer().ApiDescriptions; ApiDescription apiDescription = apiDescriptions.FirstOrDefault(api => String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase)); if (apiDescription != null) { model = GenerateApiModel(apiDescription, config); config.Properties.TryAdd(modelId, model); } } return (HelpPageApiModel)model; } private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config) { HelpPageApiModel apiModel = new HelpPageApiModel() { ApiDescription = apiDescription, }; ModelDescriptionGenerator modelGenerator = config.GetModelDescriptionGenerator(); HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); GenerateUriParameters(apiModel, modelGenerator); GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator); GenerateResourceDescription(apiModel, modelGenerator); GenerateSamples(apiModel, sampleGenerator); return apiModel; } private static void GenerateUriParameters(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) { ApiDescription apiDescription = apiModel.ApiDescription; foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) { if (apiParameter.Source == ApiParameterSource.FromUri) { HttpParameterDescriptor parameterDescriptor = apiParameter.ParameterDescriptor; Type parameterType = null; ModelDescription typeDescription = null; ComplexTypeModelDescription complexTypeDescription = null; if (parameterDescriptor != null) { parameterType = parameterDescriptor.ParameterType; typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType); complexTypeDescription = typeDescription as ComplexTypeModelDescription; } // Example: // [TypeConverter(typeof(PointConverter))] // public class Point // { // public Point(int x, int y) // { // X = x; // Y = y; // } // public int X { get; set; } // public int Y { get; set; } // } // Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection. // // public class Point // { // public int X { get; set; } // public int Y { get; set; } // } // Regular complex class Point will have properties X and Y added to UriParameters collection. if (complexTypeDescription != null && !IsBindableWithTypeConverter(parameterType)) { foreach (ParameterDescription uriParameter in complexTypeDescription.Properties) { apiModel.UriParameters.Add(uriParameter); } } else if (parameterDescriptor != null) { ParameterDescription uriParameter = AddParameterDescription(apiModel, apiParameter, typeDescription); if (!parameterDescriptor.IsOptional) { uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Required" }); } object defaultValue = parameterDescriptor.DefaultValue; if (defaultValue != null) { uriParameter.Annotations.Add(new ParameterAnnotation() { Documentation = "Default value is " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }); } } else { Debug.Assert(parameterDescriptor == null); // If parameterDescriptor is null, this is an undeclared route parameter which only occurs // when source is FromUri. Ignored in request model and among resource parameters but listed // as a simple string here. ModelDescription modelDescription = modelGenerator.GetOrCreateModelDescription(typeof(string)); AddParameterDescription(apiModel, apiParameter, modelDescription); } } } } private static bool IsBindableWithTypeConverter(Type parameterType) { if (parameterType == null) { return false; } return TypeDescriptor.GetConverter(parameterType).CanConvertFrom(typeof(string)); } private static ParameterDescription AddParameterDescription(HelpPageApiModel apiModel, ApiParameterDescription apiParameter, ModelDescription typeDescription) { ParameterDescription parameterDescription = new ParameterDescription { Name = apiParameter.Name, Documentation = apiParameter.Documentation, TypeDescription = typeDescription, }; apiModel.UriParameters.Add(parameterDescription); return parameterDescription; } private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator) { ApiDescription apiDescription = apiModel.ApiDescription; foreach (ApiParameterDescription apiParameter in apiDescription.ParameterDescriptions) { if (apiParameter.Source == ApiParameterSource.FromBody) { Type parameterType = apiParameter.ParameterDescriptor.ParameterType; apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); apiModel.RequestDocumentation = apiParameter.Documentation; } else if (apiParameter.ParameterDescriptor != null && apiParameter.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage)) { Type parameterType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); if (parameterType != null) { apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType); } } } } private static void GenerateResourceDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator) { ResponseDescription response = apiModel.ApiDescription.ResponseDescription; Type responseType = response.ResponseType ?? response.DeclaredType; if (responseType != null && responseType != typeof(void)) { apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType); } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as ErrorMessages.")] private static void GenerateSamples(HelpPageApiModel apiModel, HelpPageSampleGenerator sampleGenerator) { try { foreach (var item in sampleGenerator.GetSampleRequests(apiModel.ApiDescription)) { apiModel.SampleRequests.Add(item.Key, item.Value); LogInvalidSampleAsError(apiModel, item.Value); } foreach (var item in sampleGenerator.GetSampleResponses(apiModel.ApiDescription)) { apiModel.SampleResponses.Add(item.Key, item.Value); LogInvalidSampleAsError(apiModel, item.Value); } } catch (Exception e) { apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture, "An exception has occurred while generating the sample. Exception message: {0}", HelpPageSampleGenerator.UnwrapException(e).Message)); } } private static bool TryGetResourceParameter(ApiDescription apiDescription, HttpConfiguration config, out ApiParameterDescription parameterDescription, out Type resourceType) { parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault( p => p.Source == ApiParameterSource.FromBody || (p.ParameterDescriptor != null && p.ParameterDescriptor.ParameterType == typeof(HttpRequestMessage))); if (parameterDescription == null) { resourceType = null; return false; } resourceType = parameterDescription.ParameterDescriptor.ParameterType; if (resourceType == typeof(HttpRequestMessage)) { HelpPageSampleGenerator sampleGenerator = config.GetHelpPageSampleGenerator(); resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription); } if (resourceType == null) { parameterDescription = null; return false; } return true; } private static ModelDescriptionGenerator InitializeModelDescriptionGenerator(HttpConfiguration config) { ModelDescriptionGenerator modelGenerator = new ModelDescriptionGenerator(config); Collection apis = config.Services.GetApiExplorer().ApiDescriptions; foreach (ApiDescription api in apis) { ApiParameterDescription parameterDescription; Type parameterType; if (TryGetResourceParameter(api, config, out parameterDescription, out parameterType)) { modelGenerator.GetOrCreateModelDescription(parameterType); } } return modelGenerator; } private static void LogInvalidSampleAsError(HelpPageApiModel apiModel, object sample) { InvalidSample invalidSample = sample as InvalidSample; if (invalidSample != null) { apiModel.ErrorMessages.Add(invalidSample.ErrorMessage); } } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class CollectionModelDescription : ModelDescription { public ModelDescription ElementDescription { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs ================================================ using System.Collections.ObjectModel; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class ComplexTypeModelDescription : ModelDescription { public ComplexTypeModelDescription() { Properties = new Collection(); } public Collection Properties { get; private set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class DictionaryModelDescription : KeyValuePairModelDescription { } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class EnumTypeModelDescription : ModelDescription { public EnumTypeModelDescription() { Values = new Collection(); } public Collection Values { get; private set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class EnumValueDescription { public string Documentation { get; set; } public string Name { get; set; } public string Value { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs ================================================ using System; using System.Reflection; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public interface IModelDocumentationProvider { string GetDocumentation(MemberInfo member); string GetDocumentation(Type type); } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class KeyValuePairModelDescription : ModelDescription { public ModelDescription KeyModelDescription { get; set; } public ModelDescription ValueModelDescription { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ModelDescription.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { /// /// Describes a type model. /// public abstract class ModelDescription { public string Documentation { get; set; } public Type ModelType { get; set; } public string Name { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using System.Web.Http; using System.Web.Http.Description; using System.Xml.Serialization; using Newtonsoft.Json; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { /// /// Generates model descriptions for given types. /// public class ModelDescriptionGenerator { // Modify this to support more data annotation attributes. private readonly IDictionary> AnnotationTextGenerator = new Dictionary> { { typeof(RequiredAttribute), a => "Required" }, { typeof(RangeAttribute), a => { RangeAttribute range = (RangeAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum); } }, { typeof(MaxLengthAttribute), a => { MaxLengthAttribute maxLength = (MaxLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length); } }, { typeof(MinLengthAttribute), a => { MinLengthAttribute minLength = (MinLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length); } }, { typeof(StringLengthAttribute), a => { StringLengthAttribute strLength = (StringLengthAttribute)a; return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength); } }, { typeof(DataTypeAttribute), a => { DataTypeAttribute dataType = (DataTypeAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString()); } }, { typeof(RegularExpressionAttribute), a => { RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a; return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern); } }, }; // Modify this to add more default documentations. private readonly IDictionary DefaultTypeDocumentation = new Dictionary { { typeof(Int16), "integer" }, { typeof(Int32), "integer" }, { typeof(Int64), "integer" }, { typeof(UInt16), "unsigned integer" }, { typeof(UInt32), "unsigned integer" }, { typeof(UInt64), "unsigned integer" }, { typeof(Byte), "byte" }, { typeof(Char), "character" }, { typeof(SByte), "signed byte" }, { typeof(Uri), "URI" }, { typeof(Single), "decimal number" }, { typeof(Double), "decimal number" }, { typeof(Decimal), "decimal number" }, { typeof(String), "string" }, { typeof(Guid), "globally unique identifier" }, { typeof(TimeSpan), "time interval" }, { typeof(DateTime), "date" }, { typeof(DateTimeOffset), "date" }, { typeof(Boolean), "boolean" }, }; private Lazy _documentationProvider; public ModelDescriptionGenerator(HttpConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } _documentationProvider = new Lazy(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider); GeneratedModels = new Dictionary(StringComparer.OrdinalIgnoreCase); } public Dictionary GeneratedModels { get; private set; } private IModelDocumentationProvider DocumentationProvider { get { return _documentationProvider.Value; } } public ModelDescription GetOrCreateModelDescription(Type modelType) { if (modelType == null) { throw new ArgumentNullException("modelType"); } Type underlyingType = Nullable.GetUnderlyingType(modelType); if (underlyingType != null) { modelType = underlyingType; } ModelDescription modelDescription; string modelName = ModelNameHelper.GetModelName(modelType); if (GeneratedModels.TryGetValue(modelName, out modelDescription)) { if (modelType != modelDescription.ModelType) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " + "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.", modelName, modelDescription.ModelType.FullName, modelType.FullName)); } return modelDescription; } if (DefaultTypeDocumentation.ContainsKey(modelType)) { return GenerateSimpleTypeModelDescription(modelType); } if (modelType.IsEnum) { return GenerateEnumTypeModelDescription(modelType); } if (modelType.IsGenericType) { Type[] genericArguments = modelType.GetGenericArguments(); if (genericArguments.Length == 1) { Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments); if (enumerableType.IsAssignableFrom(modelType)) { return GenerateCollectionModelDescription(modelType, genericArguments[0]); } } if (genericArguments.Length == 2) { Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments); if (dictionaryType.IsAssignableFrom(modelType)) { return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]); } Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments); if (keyValuePairType.IsAssignableFrom(modelType)) { return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]); } } } if (modelType.IsArray) { Type elementType = modelType.GetElementType(); return GenerateCollectionModelDescription(modelType, elementType); } if (modelType == typeof(NameValueCollection)) { return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string)); } if (typeof(IDictionary).IsAssignableFrom(modelType)) { return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object)); } if (typeof(IEnumerable).IsAssignableFrom(modelType)) { return GenerateCollectionModelDescription(modelType, typeof(object)); } return GenerateComplexTypeModelDescription(modelType); } // Change this to provide different name for the member. private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute) { JsonPropertyAttribute jsonProperty = member.GetCustomAttribute(); if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName)) { return jsonProperty.PropertyName; } if (hasDataContractAttribute) { DataMemberAttribute dataMember = member.GetCustomAttribute(); if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name)) { return dataMember.Name; } } return member.Name; } private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute) { JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute(); XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute(); IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute(); NonSerializedAttribute nonSerialized = member.GetCustomAttribute(); ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute(); bool hasMemberAttribute = member.DeclaringType.IsEnum ? member.GetCustomAttribute() != null : member.GetCustomAttribute() != null; // Display member only if all the followings are true: // no JsonIgnoreAttribute // no XmlIgnoreAttribute // no IgnoreDataMemberAttribute // no NonSerializedAttribute // no ApiExplorerSettingsAttribute with IgnoreApi set to true // no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute return jsonIgnore == null && xmlIgnore == null && ignoreDataMember == null && nonSerialized == null && (apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) && (!hasDataContractAttribute || hasMemberAttribute); } private string CreateDefaultDocumentation(Type type) { string documentation; if (DefaultTypeDocumentation.TryGetValue(type, out documentation)) { return documentation; } if (DocumentationProvider != null) { documentation = DocumentationProvider.GetDocumentation(type); } return documentation; } private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel) { List annotations = new List(); IEnumerable attributes = property.GetCustomAttributes(); foreach (Attribute attribute in attributes) { Func textGenerator; if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator)) { annotations.Add( new ParameterAnnotation { AnnotationAttribute = attribute, Documentation = textGenerator(attribute) }); } } // Rearrange the annotations annotations.Sort((x, y) => { // Special-case RequiredAttribute so that it shows up on top if (x.AnnotationAttribute is RequiredAttribute) { return -1; } if (y.AnnotationAttribute is RequiredAttribute) { return 1; } // Sort the rest based on alphabetic order of the documentation return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase); }); foreach (ParameterAnnotation annotation in annotations) { propertyModel.Annotations.Add(annotation); } } private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType) { ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType); if (collectionModelDescription != null) { return new CollectionModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, ElementDescription = collectionModelDescription }; } return null; } private ModelDescription GenerateComplexTypeModelDescription(Type modelType) { ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; GeneratedModels.Add(complexModelDescription.Name, complexModelDescription); bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { if (ShouldDisplayMember(property, hasDataContractAttribute)) { ParameterDescription propertyModel = new ParameterDescription { Name = GetMemberName(property, hasDataContractAttribute) }; if (DocumentationProvider != null) { propertyModel.Documentation = DocumentationProvider.GetDocumentation(property); } GenerateAnnotations(property, propertyModel); complexModelDescription.Properties.Add(propertyModel); propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType); } } FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in fields) { if (ShouldDisplayMember(field, hasDataContractAttribute)) { ParameterDescription propertyModel = new ParameterDescription { Name = GetMemberName(field, hasDataContractAttribute) }; if (DocumentationProvider != null) { propertyModel.Documentation = DocumentationProvider.GetDocumentation(field); } complexModelDescription.Properties.Add(propertyModel); propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType); } } return complexModelDescription; } private DictionaryModelDescription GenerateDictionaryModelDescription(Type modelType, Type keyType, Type valueType) { ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); return new DictionaryModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, KeyModelDescription = keyModelDescription, ValueModelDescription = valueModelDescription }; } private EnumTypeModelDescription GenerateEnumTypeModelDescription(Type modelType) { EnumTypeModelDescription enumDescription = new EnumTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; bool hasDataContractAttribute = modelType.GetCustomAttribute() != null; foreach (FieldInfo field in modelType.GetFields(BindingFlags.Public | BindingFlags.Static)) { if (ShouldDisplayMember(field, hasDataContractAttribute)) { EnumValueDescription enumValue = new EnumValueDescription { Name = field.Name, Value = field.GetRawConstantValue().ToString() }; if (DocumentationProvider != null) { enumValue.Documentation = DocumentationProvider.GetDocumentation(field); } enumDescription.Values.Add(enumValue); } } GeneratedModels.Add(enumDescription.Name, enumDescription); return enumDescription; } private KeyValuePairModelDescription GenerateKeyValuePairModelDescription(Type modelType, Type keyType, Type valueType) { ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType); ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType); return new KeyValuePairModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, KeyModelDescription = keyModelDescription, ValueModelDescription = valueModelDescription }; } private ModelDescription GenerateSimpleTypeModelDescription(Type modelType) { SimpleTypeModelDescription simpleModelDescription = new SimpleTypeModelDescription { Name = ModelNameHelper.GetModelName(modelType), ModelType = modelType, Documentation = CreateDefaultDocumentation(modelType) }; GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription); return simpleModelDescription; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { /// /// Use this attribute to change the name of the generated for a type. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)] public sealed class ModelNameAttribute : Attribute { public ModelNameAttribute(string name) { Name = name; } public string Name { get; private set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs ================================================ using System; using System.Globalization; using System.Linq; using System.Reflection; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { internal static class ModelNameHelper { // Modify this to provide custom model name mapping. public static string GetModelName(Type type) { ModelNameAttribute modelNameAttribute = type.GetCustomAttribute(); if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name)) { return modelNameAttribute.Name; } string modelName = type.Name; if (type.IsGenericType) { // Format the generic type name to something like: GenericOfAgurment1AndArgument2 Type genericType = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); string genericTypeName = genericType.Name; // Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray(); modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames)); } return modelName; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class ParameterAnnotation { public Attribute AnnotationAttribute { get; set; } public string Documentation { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class ParameterDescription { public ParameterDescription() { Annotations = new Collection(); } public Collection Annotations { get; private set; } public string Documentation { get; set; } public string Name { get; set; } public ModelDescription TypeDescription { get; set; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions { public class SimpleTypeModelDescription : ModelDescription { } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Models/HelpPageApiModel.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Net.Http.Headers; using System.Web.Http.Description; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models { /// /// The model that represents an API displayed on the help page. /// public class HelpPageApiModel { /// /// Initializes a new instance of the class. /// public HelpPageApiModel() { UriParameters = new Collection(); SampleRequests = new Dictionary(); SampleResponses = new Dictionary(); ErrorMessages = new Collection(); } /// /// Gets or sets the that describes the API. /// public ApiDescription ApiDescription { get; set; } /// /// Gets or sets the collection that describes the URI parameters for the API. /// public Collection UriParameters { get; private set; } /// /// Gets or sets the documentation for the request. /// public string RequestDocumentation { get; set; } /// /// Gets or sets the that describes the request body. /// public ModelDescription RequestModelDescription { get; set; } /// /// Gets the request body parameter descriptions. /// public IList RequestBodyParameters { get { return GetParameterDescriptions(RequestModelDescription); } } /// /// Gets or sets the that describes the resource. /// public ModelDescription ResourceDescription { get; set; } /// /// Gets the resource property descriptions. /// public IList ResourceProperties { get { return GetParameterDescriptions(ResourceDescription); } } /// /// Gets the sample requests associated with the API. /// public IDictionary SampleRequests { get; private set; } /// /// Gets the sample responses associated with the API. /// public IDictionary SampleResponses { get; private set; } /// /// Gets the error messages associated with this model. /// public Collection ErrorMessages { get; private set; } private static IList GetParameterDescriptions(ModelDescription modelDescription) { ComplexTypeModelDescription complexTypeModelDescription = modelDescription as ComplexTypeModelDescription; if (complexTypeModelDescription != null) { return complexTypeModelDescription.Properties; } CollectionModelDescription collectionModelDescription = modelDescription as CollectionModelDescription; if (collectionModelDescription != null) { complexTypeModelDescription = collectionModelDescription.ElementDescription as ComplexTypeModelDescription; if (complexTypeModelDescription != null) { return complexTypeModelDescription.Properties; } } return null; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http.Description; using System.Xml.Linq; using Newtonsoft.Json; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This class will generate the samples for the help page. /// public class HelpPageSampleGenerator { /// /// Initializes a new instance of the class. /// public HelpPageSampleGenerator() { ActualHttpMessageTypes = new Dictionary(); ActionSamples = new Dictionary(); SampleObjects = new Dictionary(); SampleObjectFactories = new List> { DefaultSampleObjectFactory, }; } /// /// Gets CLR types that are used as the content of or . /// public IDictionary ActualHttpMessageTypes { get; internal set; } /// /// Gets the objects that are used directly as samples for certain actions. /// public IDictionary ActionSamples { get; internal set; } /// /// Gets the objects that are serialized as samples by the supported formatters. /// public IDictionary SampleObjects { get; internal set; } /// /// Gets factories for the objects that the supported formatters will serialize as samples. Processed in order, /// stopping when the factory successfully returns a non- object. /// /// /// Collection includes just initially. Use /// SampleObjectFactories.Insert(0, func) to provide an override and /// SampleObjectFactories.Add(func) to provide a fallback. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")] public IList> SampleObjectFactories { get; private set; } /// /// Gets the request body samples for a given . /// /// The . /// The samples keyed by media type. public IDictionary GetSampleRequests(ApiDescription api) { return GetSample(api, SampleDirection.Request); } /// /// Gets the response body samples for a given . /// /// The . /// The samples keyed by media type. public IDictionary GetSampleResponses(ApiDescription api) { return GetSample(api, SampleDirection.Response); } /// /// Gets the request or response body samples. /// /// The . /// The value indicating whether the sample is for a request or for a response. /// The samples keyed by media type. public virtual IDictionary GetSample(ApiDescription api, SampleDirection sampleDirection) { if (api == null) { throw new ArgumentNullException("api"); } string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = api.ActionDescriptor.ActionName; IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); Collection formatters; Type type = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, out formatters); var samples = new Dictionary(); // Use the samples provided directly for actions var actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection); foreach (var actionSample in actionSamples) { samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value)); } // Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage. // Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters. if (type != null && !typeof(HttpResponseMessage).IsAssignableFrom(type)) { object sampleObject = GetSampleObject(type); foreach (var formatter in formatters) { foreach (MediaTypeHeaderValue mediaType in formatter.SupportedMediaTypes) { if (!samples.ContainsKey(mediaType)) { object sample = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection); // If no sample found, try generate sample using formatter and sample object if (sample == null && sampleObject != null) { sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType); } samples.Add(mediaType, WrapSampleIfString(sample)); } } } } return samples; } /// /// Search for samples that are provided directly through . /// /// Name of the controller. /// Name of the action. /// The parameter names. /// The CLR type. /// The formatter. /// The media type. /// The value indicating whether the sample is for a request or for a response. /// The sample that matches the parameters. public virtual object GetActionSample(string controllerName, string actionName, IEnumerable parameterNames, Type type, MediaTypeFormatter formatter, MediaTypeHeaderValue mediaType, SampleDirection sampleDirection) { object sample; // First, try to get the sample provided for the specified mediaType, sampleDirection, controllerName, actionName and parameterNames. // If not found, try to get the sample provided for the specified mediaType, sampleDirection, controllerName and actionName regardless of the parameterNames. // If still not found, try to get the sample provided for the specified mediaType and type. // Finally, try to get the sample provided for the specified mediaType. if (ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, parameterNames), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, new[] { "*" }), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType, type), out sample) || ActionSamples.TryGetValue(new HelpPageSampleKey(mediaType), out sample)) { return sample; } return null; } /// /// Gets the sample object that will be serialized by the formatters. /// First, it will look at the . If no sample object is found, it will try to create /// one using (which wraps an ) and other /// factories in . /// /// The type. /// The sample object. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Even if all items in SampleObjectFactories throw, problem will be visible as missing sample.")] public virtual object GetSampleObject(Type type) { object sampleObject; if (!SampleObjects.TryGetValue(type, out sampleObject)) { // No specific object available, try our factories. foreach (Func factory in SampleObjectFactories) { if (factory == null) { continue; } try { sampleObject = factory(this, type); if (sampleObject != null) { break; } } catch { // Ignore any problems encountered in the factory; go on to the next one (if any). } } } return sampleObject; } /// /// Resolves the actual type of passed to the in an action. /// /// The . /// The type. public virtual Type ResolveHttpRequestMessageType(ApiDescription api) { string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = api.ActionDescriptor.ActionName; IEnumerable parameterNames = api.ParameterDescriptions.Select(p => p.Name); Collection formatters; return ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, out formatters); } /// /// Resolves the type of the action parameter or return value when or is used. /// /// The . /// Name of the controller. /// Name of the action. /// The parameter names. /// The value indicating whether the sample is for a request or a response. /// The formatters. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is only used in advanced scenarios.")] public virtual Type ResolveType(ApiDescription api, string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection, out Collection formatters) { if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) { throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); } if (api == null) { throw new ArgumentNullException("api"); } Type type; if (ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), out type) || ActualHttpMessageTypes.TryGetValue(new HelpPageSampleKey(sampleDirection, controllerName, actionName, new[] { "*" }), out type)) { // Re-compute the supported formatters based on type Collection newFormatters = new Collection(); foreach (var formatter in api.ActionDescriptor.Configuration.Formatters) { if (IsFormatSupported(sampleDirection, formatter, type)) { newFormatters.Add(formatter); } } formatters = newFormatters; } else { switch (sampleDirection) { case SampleDirection.Request: ApiParameterDescription requestBodyParameter = api.ParameterDescriptions.FirstOrDefault(p => p.Source == ApiParameterSource.FromBody); type = requestBodyParameter == null ? null : requestBodyParameter.ParameterDescriptor.ParameterType; formatters = api.SupportedRequestBodyFormatters; break; case SampleDirection.Response: default: type = api.ResponseDescription.ResponseType ?? api.ResponseDescription.DeclaredType; formatters = api.SupportedResponseFormatters; break; } } return type; } /// /// Writes the sample object using formatter. /// /// The formatter. /// The value. /// The type. /// Type of the media. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The exception is recorded as InvalidSample.")] public virtual object WriteSampleObjectUsingFormatter(MediaTypeFormatter formatter, object value, Type type, MediaTypeHeaderValue mediaType) { if (formatter == null) { throw new ArgumentNullException("formatter"); } if (mediaType == null) { throw new ArgumentNullException("mediaType"); } object sample = String.Empty; MemoryStream ms = null; HttpContent content = null; try { if (formatter.CanWriteType(type)) { ms = new MemoryStream(); content = new ObjectContent(type, value, formatter, mediaType); formatter.WriteToStreamAsync(type, value, ms, content, null).Wait(); ms.Position = 0; StreamReader reader = new StreamReader(ms); string serializedSampleString = reader.ReadToEnd(); if (mediaType.MediaType.ToUpperInvariant().Contains("XML")) { serializedSampleString = TryFormatXml(serializedSampleString); } else if (mediaType.MediaType.ToUpperInvariant().Contains("JSON")) { serializedSampleString = TryFormatJson(serializedSampleString); } sample = new TextSample(serializedSampleString); } else { sample = new InvalidSample(String.Format( CultureInfo.CurrentCulture, "Failed to generate the sample for media type '{0}'. Cannot use formatter '{1}' to write type '{2}'.", mediaType, formatter.GetType().Name, type.Name)); } } catch (Exception e) { sample = new InvalidSample(String.Format( CultureInfo.CurrentCulture, "An exception has occurred while using the formatter '{0}' to generate sample for media type '{1}'. Exception message: {2}", formatter.GetType().Name, mediaType.MediaType, UnwrapException(e).Message)); } finally { if (ms != null) { ms.Dispose(); } if (content != null) { content.Dispose(); } } return sample; } internal static Exception UnwrapException(Exception exception) { AggregateException aggregateException = exception as AggregateException; if (aggregateException != null) { return aggregateException.Flatten().InnerException; } return exception; } // Default factory for sample objects private static object DefaultSampleObjectFactory(HelpPageSampleGenerator sampleGenerator, Type type) { // Try to create a default sample object ObjectGenerator objectGenerator = new ObjectGenerator(); return objectGenerator.GenerateObject(type); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] private static string TryFormatJson(string str) { try { object parsedJson = JsonConvert.DeserializeObject(str); return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); } catch { // can't parse JSON, return the original string return str; } } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] private static string TryFormatXml(string str) { try { XDocument xml = XDocument.Parse(str); return xml.ToString(); } catch { // can't parse XML, return the original string return str; } } private static bool IsFormatSupported(SampleDirection sampleDirection, MediaTypeFormatter formatter, Type type) { switch (sampleDirection) { case SampleDirection.Request: return formatter.CanReadType(type); case SampleDirection.Response: return formatter.CanWriteType(type); } return false; } private IEnumerable> GetAllActionSamples(string controllerName, string actionName, IEnumerable parameterNames, SampleDirection sampleDirection) { HashSet parameterNamesSet = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); foreach (var sample in ActionSamples) { HelpPageSampleKey sampleKey = sample.Key; if (String.Equals(controllerName, sampleKey.ControllerName, StringComparison.OrdinalIgnoreCase) && String.Equals(actionName, sampleKey.ActionName, StringComparison.OrdinalIgnoreCase) && (sampleKey.ParameterNames.SetEquals(new[] { "*" }) || parameterNamesSet.SetEquals(sampleKey.ParameterNames)) && sampleDirection == sampleKey.SampleDirection) { yield return sample; } } } private static object WrapSampleIfString(object sample) { string stringSample = sample as string; if (stringSample != null) { return new TextSample(stringSample); } return sample; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Net.Http.Headers; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This is used to identify the place where the sample should be applied. /// public class HelpPageSampleKey { /// /// Creates a new based on media type. /// /// The media type. public HelpPageSampleKey(MediaTypeHeaderValue mediaType) { if (mediaType == null) { throw new ArgumentNullException("mediaType"); } ActionName = String.Empty; ControllerName = String.Empty; MediaType = mediaType; ParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase); } /// /// Creates a new based on media type and CLR type. /// /// The media type. /// The CLR type. public HelpPageSampleKey(MediaTypeHeaderValue mediaType, Type type) : this(mediaType) { if (type == null) { throw new ArgumentNullException("type"); } ParameterType = type; } /// /// Creates a new based on , controller name, action name and parameter names. /// /// The . /// Name of the controller. /// Name of the action. /// The parameter names. public HelpPageSampleKey(SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) { if (!Enum.IsDefined(typeof(SampleDirection), sampleDirection)) { throw new InvalidEnumArgumentException("sampleDirection", (int)sampleDirection, typeof(SampleDirection)); } if (controllerName == null) { throw new ArgumentNullException("controllerName"); } if (actionName == null) { throw new ArgumentNullException("actionName"); } if (parameterNames == null) { throw new ArgumentNullException("parameterNames"); } ControllerName = controllerName; ActionName = actionName; ParameterNames = new HashSet(parameterNames, StringComparer.OrdinalIgnoreCase); SampleDirection = sampleDirection; } /// /// Creates a new based on media type, , controller name, action name and parameter names. /// /// The media type. /// The . /// Name of the controller. /// Name of the action. /// The parameter names. public HelpPageSampleKey(MediaTypeHeaderValue mediaType, SampleDirection sampleDirection, string controllerName, string actionName, IEnumerable parameterNames) : this(sampleDirection, controllerName, actionName, parameterNames) { if (mediaType == null) { throw new ArgumentNullException("mediaType"); } MediaType = mediaType; } /// /// Gets the name of the controller. /// /// /// The name of the controller. /// public string ControllerName { get; private set; } /// /// Gets the name of the action. /// /// /// The name of the action. /// public string ActionName { get; private set; } /// /// Gets the media type. /// /// /// The media type. /// public MediaTypeHeaderValue MediaType { get; private set; } /// /// Gets the parameter names. /// public HashSet ParameterNames { get; private set; } public Type ParameterType { get; private set; } /// /// Gets the . /// public SampleDirection? SampleDirection { get; private set; } public override bool Equals(object obj) { HelpPageSampleKey otherKey = obj as HelpPageSampleKey; if (otherKey == null) { return false; } return String.Equals(ControllerName, otherKey.ControllerName, StringComparison.OrdinalIgnoreCase) && String.Equals(ActionName, otherKey.ActionName, StringComparison.OrdinalIgnoreCase) && (MediaType == otherKey.MediaType || (MediaType != null && MediaType.Equals(otherKey.MediaType))) && ParameterType == otherKey.ParameterType && SampleDirection == otherKey.SampleDirection && ParameterNames.SetEquals(otherKey.ParameterNames); } public override int GetHashCode() { int hashCode = ControllerName.ToUpperInvariant().GetHashCode() ^ ActionName.ToUpperInvariant().GetHashCode(); if (MediaType != null) { hashCode ^= MediaType.GetHashCode(); } if (SampleDirection != null) { hashCode ^= SampleDirection.GetHashCode(); } if (ParameterType != null) { hashCode ^= ParameterType.GetHashCode(); } foreach (string parameterName in ParameterNames) { hashCode ^= parameterName.ToUpperInvariant().GetHashCode(); } return hashCode; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/ImageSample.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This represents an image sample on the help page. There's a display template named ImageSample associated with this class. /// public class ImageSample { /// /// Initializes a new instance of the class. /// /// The URL of an image. public ImageSample(string src) { if (src == null) { throw new ArgumentNullException("src"); } Src = src; } public string Src { get; private set; } public override bool Equals(object obj) { ImageSample other = obj as ImageSample; return other != null && Src == other.Src; } public override int GetHashCode() { return Src.GetHashCode(); } public override string ToString() { return Src; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/InvalidSample.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. /// public class InvalidSample { public InvalidSample(string errorMessage) { if (errorMessage == null) { throw new ArgumentNullException("errorMessage"); } ErrorMessage = errorMessage; } public string ErrorMessage { get; private set; } public override bool Equals(object obj) { InvalidSample other = obj as InvalidSample; return other != null && ErrorMessage == other.ErrorMessage; } public override int GetHashCode() { return ErrorMessage.GetHashCode(); } public override string ToString() { return ErrorMessage; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/ObjectGenerator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This class will create an object of a given type and populate it with sample data. /// public class ObjectGenerator { internal const int DefaultCollectionSize = 2; private readonly SimpleTypeObjectGenerator SimpleObjectGenerator = new SimpleTypeObjectGenerator(); /// /// Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types: /// Simple types: , , , , , etc. /// Complex types: POCO types. /// Nullables: . /// Arrays: arrays of simple types or complex types. /// Key value pairs: /// Tuples: , , etc /// Dictionaries: or anything deriving from . /// Collections: , , , , , or anything deriving from or . /// Queryables: , . /// /// The type. /// An object of the given type. public object GenerateObject(Type type) { return GenerateObject(type, new Dictionary()); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Here we just want to return null if anything goes wrong.")] private object GenerateObject(Type type, Dictionary createdObjectReferences) { try { if (SimpleTypeObjectGenerator.CanGenerateObject(type)) { return SimpleObjectGenerator.GenerateObject(type); } if (type.IsArray) { return GenerateArray(type, DefaultCollectionSize, createdObjectReferences); } if (type.IsGenericType) { return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IDictionary)) { return GenerateDictionary(typeof(Hashtable), DefaultCollectionSize, createdObjectReferences); } if (typeof(IDictionary).IsAssignableFrom(type)) { return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IList) || type == typeof(IEnumerable) || type == typeof(ICollection)) { return GenerateCollection(typeof(ArrayList), DefaultCollectionSize, createdObjectReferences); } if (typeof(IList).IsAssignableFrom(type)) { return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences); } if (type == typeof(IQueryable)) { return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences); } if (type.IsEnum) { return GenerateEnum(type); } if (type.IsPublic || type.IsNestedPublic) { return GenerateComplexObject(type, createdObjectReferences); } } catch { // Returns null if anything fails return null; } return null; } private static object GenerateGenericType(Type type, int collectionSize, Dictionary createdObjectReferences) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Nullable<>)) { return GenerateNullable(type, createdObjectReferences); } if (genericTypeDefinition == typeof(KeyValuePair<,>)) { return GenerateKeyValuePair(type, createdObjectReferences); } if (IsTuple(genericTypeDefinition)) { return GenerateTuple(type, createdObjectReferences); } Type[] genericArguments = type.GetGenericArguments(); if (genericArguments.Length == 1) { if (genericTypeDefinition == typeof(IList<>) || genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>)) { Type collectionType = typeof(List<>).MakeGenericType(genericArguments); return GenerateCollection(collectionType, collectionSize, createdObjectReferences); } if (genericTypeDefinition == typeof(IQueryable<>)) { return GenerateQueryable(type, collectionSize, createdObjectReferences); } Type closedCollectionType = typeof(ICollection<>).MakeGenericType(genericArguments[0]); if (closedCollectionType.IsAssignableFrom(type)) { return GenerateCollection(type, collectionSize, createdObjectReferences); } } if (genericArguments.Length == 2) { if (genericTypeDefinition == typeof(IDictionary<,>)) { Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(genericArguments); return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences); } Type closedDictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments[0], genericArguments[1]); if (closedDictionaryType.IsAssignableFrom(type)) { return GenerateDictionary(type, collectionSize, createdObjectReferences); } } if (type.IsPublic || type.IsNestedPublic) { return GenerateComplexObject(type, createdObjectReferences); } return null; } private static object GenerateTuple(Type type, Dictionary createdObjectReferences) { Type[] genericArgs = type.GetGenericArguments(); object[] parameterValues = new object[genericArgs.Length]; bool failedToCreateTuple = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < genericArgs.Length; i++) { parameterValues[i] = objectGenerator.GenerateObject(genericArgs[i], createdObjectReferences); failedToCreateTuple &= parameterValues[i] == null; } if (failedToCreateTuple) { return null; } object result = Activator.CreateInstance(type, parameterValues); return result; } private static bool IsTuple(Type genericTypeDefinition) { return genericTypeDefinition == typeof(Tuple<>) || genericTypeDefinition == typeof(Tuple<,>) || genericTypeDefinition == typeof(Tuple<,,>) || genericTypeDefinition == typeof(Tuple<,,,>) || genericTypeDefinition == typeof(Tuple<,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,,>) || genericTypeDefinition == typeof(Tuple<,,,,,,,>); } private static object GenerateKeyValuePair(Type keyValuePairType, Dictionary createdObjectReferences) { Type[] genericArgs = keyValuePairType.GetGenericArguments(); Type typeK = genericArgs[0]; Type typeV = genericArgs[1]; ObjectGenerator objectGenerator = new ObjectGenerator(); object keyObject = objectGenerator.GenerateObject(typeK, createdObjectReferences); object valueObject = objectGenerator.GenerateObject(typeV, createdObjectReferences); if (keyObject == null && valueObject == null) { // Failed to create key and values return null; } object result = Activator.CreateInstance(keyValuePairType, keyObject, valueObject); return result; } private static object GenerateArray(Type arrayType, int size, Dictionary createdObjectReferences) { Type type = arrayType.GetElementType(); Array result = Array.CreateInstance(type, size); bool areAllElementsNull = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object element = objectGenerator.GenerateObject(type, createdObjectReferences); result.SetValue(element, i); areAllElementsNull &= element == null; } if (areAllElementsNull) { return null; } return result; } private static object GenerateDictionary(Type dictionaryType, int size, Dictionary createdObjectReferences) { Type typeK = typeof(object); Type typeV = typeof(object); if (dictionaryType.IsGenericType) { Type[] genericArgs = dictionaryType.GetGenericArguments(); typeK = genericArgs[0]; typeV = genericArgs[1]; } object result = Activator.CreateInstance(dictionaryType); MethodInfo addMethod = dictionaryType.GetMethod("Add") ?? dictionaryType.GetMethod("TryAdd"); MethodInfo containsMethod = dictionaryType.GetMethod("Contains") ?? dictionaryType.GetMethod("ContainsKey"); ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object newKey = objectGenerator.GenerateObject(typeK, createdObjectReferences); if (newKey == null) { // Cannot generate a valid key return null; } bool containsKey = (bool)containsMethod.Invoke(result, new object[] { newKey }); if (!containsKey) { object newValue = objectGenerator.GenerateObject(typeV, createdObjectReferences); addMethod.Invoke(result, new object[] { newKey, newValue }); } } return result; } private static object GenerateEnum(Type enumType) { Array possibleValues = Enum.GetValues(enumType); if (possibleValues.Length > 0) { return possibleValues.GetValue(0); } return null; } private static object GenerateQueryable(Type queryableType, int size, Dictionary createdObjectReferences) { bool isGeneric = queryableType.IsGenericType; object list; if (isGeneric) { Type listType = typeof(List<>).MakeGenericType(queryableType.GetGenericArguments()); list = GenerateCollection(listType, size, createdObjectReferences); } else { list = GenerateArray(typeof(object[]), size, createdObjectReferences); } if (list == null) { return null; } if (isGeneric) { Type argumentType = typeof(IEnumerable<>).MakeGenericType(queryableType.GetGenericArguments()); MethodInfo asQueryableMethod = typeof(Queryable).GetMethod("AsQueryable", new[] { argumentType }); return asQueryableMethod.Invoke(null, new[] { list }); } return Queryable.AsQueryable((IEnumerable)list); } private static object GenerateCollection(Type collectionType, int size, Dictionary createdObjectReferences) { Type type = collectionType.IsGenericType ? collectionType.GetGenericArguments()[0] : typeof(object); object result = Activator.CreateInstance(collectionType); MethodInfo addMethod = collectionType.GetMethod("Add"); bool areAllElementsNull = true; ObjectGenerator objectGenerator = new ObjectGenerator(); for (int i = 0; i < size; i++) { object element = objectGenerator.GenerateObject(type, createdObjectReferences); addMethod.Invoke(result, new object[] { element }); areAllElementsNull &= element == null; } if (areAllElementsNull) { return null; } return result; } private static object GenerateNullable(Type nullableType, Dictionary createdObjectReferences) { Type type = nullableType.GetGenericArguments()[0]; ObjectGenerator objectGenerator = new ObjectGenerator(); return objectGenerator.GenerateObject(type, createdObjectReferences); } private static object GenerateComplexObject(Type type, Dictionary createdObjectReferences) { object result = null; if (createdObjectReferences.TryGetValue(type, out result)) { // The object has been created already, just return it. This will handle the circular reference case. return result; } if (type.IsValueType) { result = Activator.CreateInstance(type); } else { ConstructorInfo defaultCtor = type.GetConstructor(Type.EmptyTypes); if (defaultCtor == null) { // Cannot instantiate the type because it doesn't have a default constructor return null; } result = defaultCtor.Invoke(new object[0]); } createdObjectReferences.Add(type, result); SetPublicProperties(type, result, createdObjectReferences); SetPublicFields(type, result, createdObjectReferences); return result; } private static void SetPublicProperties(Type type, object obj, Dictionary createdObjectReferences) { PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); ObjectGenerator objectGenerator = new ObjectGenerator(); foreach (PropertyInfo property in properties) { if (property.CanWrite) { object propertyValue = objectGenerator.GenerateObject(property.PropertyType, createdObjectReferences); property.SetValue(obj, propertyValue, null); } } } private static void SetPublicFields(Type type, object obj, Dictionary createdObjectReferences) { FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); ObjectGenerator objectGenerator = new ObjectGenerator(); foreach (FieldInfo field in fields) { object fieldValue = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences); field.SetValue(obj, fieldValue); } } private class SimpleTypeObjectGenerator { private long _index = 0; private static readonly Dictionary> DefaultGenerators = InitializeGenerators(); [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These are simple type factories and cannot be split up.")] private static Dictionary> InitializeGenerators() { return new Dictionary> { { typeof(Boolean), index => true }, { typeof(Byte), index => (Byte)64 }, { typeof(Char), index => (Char)65 }, { typeof(DateTime), index => DateTime.Now }, { typeof(DateTimeOffset), index => new DateTimeOffset(DateTime.Now) }, { typeof(DBNull), index => DBNull.Value }, { typeof(Decimal), index => (Decimal)index }, { typeof(Double), index => (Double)(index + 0.1) }, { typeof(Guid), index => Guid.NewGuid() }, { typeof(Int16), index => (Int16)(index % Int16.MaxValue) }, { typeof(Int32), index => (Int32)(index % Int32.MaxValue) }, { typeof(Int64), index => (Int64)index }, { typeof(Object), index => new object() }, { typeof(SByte), index => (SByte)64 }, { typeof(Single), index => (Single)(index + 0.1) }, { typeof(String), index => { return String.Format(CultureInfo.CurrentCulture, "sample string {0}", index); } }, { typeof(TimeSpan), index => { return TimeSpan.FromTicks(1234567); } }, { typeof(UInt16), index => (UInt16)(index % UInt16.MaxValue) }, { typeof(UInt32), index => (UInt32)(index % UInt32.MaxValue) }, { typeof(UInt64), index => (UInt64)index }, { typeof(Uri), index => { return new Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index)); } }, }; } public static bool CanGenerateObject(Type type) { return DefaultGenerators.ContainsKey(type); } public object GenerateObject(Type type) { return DefaultGenerators[type](++_index); } } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/SampleDirection.cs ================================================ namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// Indicates whether the sample is used for request or response /// public enum SampleDirection { Request = 0, Response } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/SampleGeneration/TextSample.cs ================================================ using System; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. /// public class TextSample { public TextSample(string text) { if (text == null) { throw new ArgumentNullException("text"); } Text = text; } public string Text { get; private set; } public override bool Equals(object obj) { TextSample other = obj as TextSample; return other != null && Text == other.Text; } public override int GetHashCode() { return Text.GetHashCode(); } public override string ToString() { return Text; } } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/Api.cshtml ================================================ @using System.Web.Http @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @model HelpPageApiModel @{ var description = Model.ApiDescription; ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath; }
@Html.DisplayForModel()
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Controllers @using System.Web.Http.Description @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @model IGrouping @{ var controllerDocumentation = ViewBag.DocumentationProvider != null ? ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : null; }

@Model.Key.ControllerName

@if (!String.IsNullOrEmpty(controllerDocumentation)) {

@controllerDocumentation

} @foreach (var api in Model) { }
APIDescription
@api.HttpMethod.Method @api.RelativePath @if (api.Documentation != null) {

@api.Documentation

} else {

No documentation available.

}
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model CollectionModelDescription @if (Model.ElementDescription is ComplexTypeModelDescription) { @Html.DisplayFor(m => m.ElementDescription) } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model ComplexTypeModelDescription @Html.DisplayFor(m => m.Properties, "Parameters") ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model DictionaryModelDescription Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model EnumTypeModelDescription

Possible enumeration values:

@foreach (EnumValueDescription value in Model.Values) { }
NameValueDescription
@value.Name

@value.Value

@value.Documentation

================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Description @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model HelpPageApiModel @{ ApiDescription description = Model.ApiDescription; }

@description.HttpMethod.Method @description.RelativePath

@description.Documentation

Request Information

URI Parameters

@Html.DisplayFor(m => m.UriParameters, "Parameters")

Body Parameters

@Model.RequestDocumentation

@if (Model.RequestModelDescription != null) { @Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription }) if (Model.RequestBodyParameters != null) { @Html.DisplayFor(m => m.RequestBodyParameters, "Parameters") } } else {

None.

} @if (Model.SampleRequests.Count > 0) {

Request Formats

@Html.DisplayFor(m => m.SampleRequests, "Samples") }

Response Information

Resource Description

@description.ResponseDescription.Documentation

@if (Model.ResourceDescription != null) { @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }) if (Model.ResourceProperties != null) { @Html.DisplayFor(m => m.ResourceProperties, "Parameters") } } else {

None.

} @if (Model.SampleResponses.Count > 0) {

Response Formats

@Html.DisplayFor(m => m.SampleResponses, "Samples") }
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage @model ImageSample ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage @model InvalidSample @if (HttpContext.Current.IsDebuggingEnabled) {

@Model.ErrorMessage

} else {

Sample not available.

} ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model KeyValuePairModelDescription Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model Type @{ ModelDescription modelDescription = ViewBag.modelDescription; if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription) { if (Model == typeof(Object)) { @:Object } else { @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null) } } else if (modelDescription is CollectionModelDescription) { var collectionDescription = modelDescription as CollectionModelDescription; var elementDescription = collectionDescription.ElementDescription; @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) } else { @Html.DisplayFor(m => modelDescription) } } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml ================================================ @using System.Collections.Generic @using System.Collections.ObjectModel @using System.Web.Http.Description @using System.Threading @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model IList @if (Model.Count > 0) { @foreach (ParameterDescription parameter in Model) { ModelDescription modelDescription = parameter.TypeDescription; }
NameDescriptionTypeAdditional information
@parameter.Name

@parameter.Documentation

@Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription }) @if (parameter.Annotations.Count > 0) { foreach (var annotation in parameter.Annotations) {

@annotation.Documentation

} } else {

None.

}
} else {

None.

} ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml ================================================ @using System.Net.Http.Headers @model Dictionary @{ // Group the samples into a single tab if they are the same. Dictionary samples = Model.GroupBy(pair => pair.Value).ToDictionary( pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()), pair => pair.Key); var mediaTypes = samples.Keys; }
@foreach (var mediaType in mediaTypes) {

@mediaType

Sample: @{ var sample = samples[mediaType]; if (sample == null) {

Sample not available.

} else { @Html.DisplayFor(s => sample); } }
}
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model SimpleTypeModelDescription @Model.Documentation ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml ================================================ @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage @model TextSample
@Model.Text
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/Index.cshtml ================================================ @using System.Web.Http @using System.Web.Http.Controllers @using System.Web.Http.Description @using System.Collections.ObjectModel @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @model Collection @{ ViewBag.Title = "ASP.NET Web API Help Page"; // Group APIs by controller ILookup apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor); }

@ViewBag.Title

@foreach (var group in apiGroups) { @Html.DisplayFor(m => group, "ApiGroup") }
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Help/ResourceModel.cshtml ================================================ @using System.Web.Http @using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @model ModelDescription

@Model.Name

@Model.Documentation

@Html.DisplayFor(m => Model)
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Shared/_Layout.cshtml ================================================  @ViewBag.Title @RenderSection("scripts", required: false) @RenderBody() ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/Web.config ================================================ 
================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/Views/_ViewStart.cshtml ================================================ @{ // Change the Layout path below to blend the look and feel of the help page with your existing web pages. Layout = "~/Areas/HelpPage/Views/Shared/_Layout.cshtml"; } ================================================ FILE: src/WebApiHelpPage/Areas/HelpPage/XmlDocumentationProvider.cs ================================================ using System; using System.Globalization; using System.Linq; using System.Reflection; using System.Web.Http.Controllers; using System.Web.Http.Description; using System.Xml.XPath; using ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions; namespace ROOT_PROJECT_NAMESPACE.Areas.HelpPage { /// /// A custom that reads the API documentation from an XML documentation file. /// public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider { private XPathNavigator _documentNavigator; private const string TypeExpression = "/doc/members/member[@name='T:{0}']"; private const string MethodExpression = "/doc/members/member[@name='M:{0}']"; private const string PropertyExpression = "/doc/members/member[@name='P:{0}']"; private const string FieldExpression = "/doc/members/member[@name='F:{0}']"; private const string ParameterExpression = "param[@name='{0}']"; /// /// Initializes a new instance of the class. /// /// The physical path to XML document. public XmlDocumentationProvider(string documentPath) { if (documentPath == null) { throw new ArgumentNullException("documentPath"); } XPathDocument xpath = new XPathDocument(documentPath); _documentNavigator = xpath.CreateNavigator(); } public string GetDocumentation(HttpControllerDescriptor controllerDescriptor) { XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType); return GetTagValue(typeNode, "summary"); } public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) { XPathNavigator methodNode = GetMethodNode(actionDescriptor); return GetTagValue(methodNode, "summary"); } public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) { ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor; if (reflectedParameterDescriptor != null) { XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor); if (methodNode != null) { string parameterName = reflectedParameterDescriptor.ParameterInfo.Name; XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)); if (parameterNode != null) { return parameterNode.Value.Trim(); } } } return null; } public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor) { XPathNavigator methodNode = GetMethodNode(actionDescriptor); return GetTagValue(methodNode, "returns"); } public string GetDocumentation(MemberInfo member) { string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name); string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression; string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName); XPathNavigator propertyNode = _documentNavigator.SelectSingleNode(selectExpression); return GetTagValue(propertyNode, "summary"); } public string GetDocumentation(Type type) { XPathNavigator typeNode = GetTypeNode(type); return GetTagValue(typeNode, "summary"); } private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor) { ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; if (reflectedActionDescriptor != null) { string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); return _documentNavigator.SelectSingleNode(selectExpression); } return null; } private static string GetMemberName(MethodInfo method) { string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), method.Name); ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != 0) { string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray(); name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)); } return name; } private static string GetTagValue(XPathNavigator parentNode, string tagName) { if (parentNode != null) { XPathNavigator node = parentNode.SelectSingleNode(tagName); if (node != null) { return node.Value.Trim(); } } return null; } private XPathNavigator GetTypeNode(Type type) { string controllerTypeName = GetTypeName(type); string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName); return _documentNavigator.SelectSingleNode(selectExpression); } private static string GetTypeName(Type type) { string name = type.FullName; if (type.IsGenericType) { // Format the generic type name to something like: Generic{System.Int32,System.String} Type genericType = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); string genericTypeName = genericType.FullName; // Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray(); name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames)); } if (type.IsNested) { // Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. name = name.Replace("+", "."); } return name; } } } ================================================ FILE: src/WebApiHelpPage/GlobalSuppressions.cs ================================================ // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NAMESPACE", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NAMESPACE", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NAMESPACE", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJECT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJECT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJECT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ROOT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ROOT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ROOT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Controllers", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "config", Scope = "member", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.HelpPageConfig.#Register(System.Web.Http.HttpConfiguration)", Justification = "This parameter is part of the template and could be used later.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "The assembly will not be deployed because we distribute the source code.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Web.FxCop", "MW1201:DoNotCallProblematicMethodsOnTaskRule", Scope = "member", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.HelpPageSampleGenerator.#WriteSampleObjectUsingFormatter(System.Net.Http.Formatting.MediaTypeFormatter,System.Object,System.Type,System.Net.Http.Headers.MediaTypeHeaderValue)", Justification = "This is an internal FxCop rule and we ship the source file.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ROOT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJECT", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NAMESPACE", Scope = "namespace", Target = "ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions", Justification = "This namespace will be replaced with the project namespace when the help page is installed.")] ================================================ FILE: src/WebApiHelpPage/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("WebApiHelpPage")] [assembly: AssemblyDescription("")] [assembly: Guid("aa22bcbc-83b3-466d-a421-9c561e3625b7")] ================================================ FILE: src/WebApiHelpPage/Settings.StyleCop ================================================ False ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ApiDescriptionExtensions.vb ================================================ Imports System Imports System.Runtime.CompilerServices Imports System.Text Imports System.Web Imports System.Web.Http.Description Namespace Areas.HelpPage Public Module ApiDescriptionExtensions ''' '''Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" ''' '''The . '''The ID as a string. Public Function GetFriendlyId(ByVal description As ApiDescription) As String Dim path As String = description.RelativePath Dim urlParts() As String = path.Split("?"c) Dim localPath As String = urlParts(0) Dim queryKeyString As String = Nothing If (urlParts.Length > 1) Then Dim query As String = urlParts(1) Dim queryKeys() As String = HttpUtility.ParseQueryString(query).AllKeys queryKeyString = String.Join("_", queryKeys) End If Dim friendlyPath As New StringBuilder friendlyPath.AppendFormat("{0}-{1}", description.HttpMethod.Method, localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)) If (Not queryKeyString Is Nothing) Then friendlyPath.AppendFormat("_{0}", queryKeyString.Replace(".", "-")) End If GetFriendlyId = friendlyPath.ToString() End Function End Module End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/App_Start/HelpPageConfig.vb ================================================ ' Uncomment the following to provide samples for PageResult(Of T). Must also add the Microsoft.AspNet.WebApi.OData ' package to your project. ''#Const Handle_PageResultOfT = 1 Imports System Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics Imports System.Diagnostics.CodeAnalysis Imports System.Linq Imports System.Net.Http.Headers Imports System.Reflection Imports System.Web Imports System.Web.Http #If Handle_PageResultOfT Then Imports System.Web.Http.OData #End If Namespace Areas.HelpPage ''' ''' Use this class to customize the Help Page. ''' For example you can set a custom to supply the documentation ''' or you can provide the samples for the requests/responses. ''' Public Module HelpPageConfig Public Sub Register(config As HttpConfiguration) '' Uncomment the following to use the documentation from XML documentation file. 'config.SetDocumentationProvider(New XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))) '' Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. '' Also, the string arrays will be used for IEnumerable(Of String). The sample objects will be serialized into different media type '' formats by the available formatters. 'config.SetSampleObjects(New Dictionary(Of Type, Object) From '{ ' {GetType(String), "sample string"}, ' {GetType(IEnumerable(Of String)), New String() {"sample 1", "sample 2"}} '}) ' Extend the following to provide factories for types not handled automatically (those lacking parameterless ' constructors) or for which you prefer to use non-default property values. Line below provides a fallback ' since automatic handling will fail and GeneratePageResult handles only a single type. #If Handle_PageResultOfT Then config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(AddressOf GeneratePageResult) #End If ' Extend the following to use a preset object directly as the sample for all actions that support a media ' type, regardless of the body parameter or return type. The lines below avoid display of binary content. ' The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. config.SetSampleForMediaType( New TextSample("Binary JSON content. See http://bsonspec.org for details."), New MediaTypeHeaderValue("application/bson")) '' Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format '' and have IEnumerable(Of String) as the body parameter or return type. 'config.SetSampleForType("[0]=foo&[1]=bar", New MediaTypeHeaderValue("application/x-www-form-urlencoded"), GetType(IEnumerable(Of String))) '' Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" '' and action named "Put". 'config.SetSampleRequest("1234", New MediaTypeHeaderValue("text/plain"), "Values", "Put") '' Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" '' on the controller named "Values" and action named "Get" with parameter "id". 'config.SetSampleResponse(New ImageSample("../images/aspNetHome.png"), New MediaTypeHeaderValue("image/png"), "Values", "Get", "id") '' Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent(Of string). '' The sample will be generated as if the controller named "Values" and action named "Get" were having String as the body parameter. 'config.SetActualRequestType(GetType(String), "Values", "Get") '' Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent(Of String). '' The sample will be generated as if the controller named "Values" and action named "Post" were returning a String. 'config.SetActualResponseType(GetType(String), "Values", "Post") End Sub #If Handle_PageResultOfT Then Private Function GeneratePageResult(sampleGenerator As HelpPageSampleGenerator, type As Type) As Object If type.IsGenericType Then Dim openGenericType As Type = type.GetGenericTypeDefinition() If openGenericType = GetType(PageResult(Of )) Then ' Get the T in PageResult(Of T) Dim typeParameters() As Type = type.GetGenericArguments() Debug.Assert(typeParameters.Length = 1) ' Create an enumeration to pass as the first parameter to the PageResult(Of T) constuctor Dim itemsType As Type = GetType(List(Of )).MakeGenericType(typeParameters) Dim items As Object = sampleGenerator.GetSampleObject(itemsType) ' Fill in the other information needed to invoke the PageResult(Of T) constuctor Dim parameterTypes() As Type = New Type() {itemsType, GetType(Uri), GetType(Long?)} Dim parameters() As Object = New Object() {items, Nothing, CLng(ObjectGenerator.DefaultCollectionSize)} ' Call PageResult(items As IEnumerable(Of T), nextPageLink As Uri, count As Long?) constructor Dim constructor As ConstructorInfo = type.GetConstructor(parameterTypes) Return constructor.Invoke(parameters) End If End If Return Nothing End Function #End If End Module End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Controllers/HelpController.vb ================================================ Imports System Imports System.Web.Http Imports System.Web.Mvc Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage.Controllers ''' ''' The controller that will handle requests for the help page. ''' Public Class HelpController Inherits Controller Private Const ErrorViewName As String = "Error" Private httpConfiguration As HttpConfiguration Public Sub New() Me.New(GlobalConfiguration.Configuration) End Sub Public Sub New(config As HttpConfiguration) Configuration = config End Sub Public Property Configuration As HttpConfiguration Get Return httpConfiguration End Get Private Set(value As HttpConfiguration) httpConfiguration = value End Set End Property Public Function Index() As ActionResult ViewData("DocumentationProvider") = Configuration.Services.GetDocumentationProvider() Return View(Configuration.Services.GetApiExplorer().ApiDescriptions) End Function Public Function Api(apiId As String) As ActionResult If (Not String.IsNullOrEmpty(apiId)) Then Dim apiModel As HelpPageApiModel = Configuration.GetHelpPageApiModel(apiId) If (Not apiModel Is Nothing) Then Return View(apiModel) End If End If Return View(ErrorViewName) End Function Public Function ResourceModel(modelName As String) As ActionResult If Not [String].IsNullOrEmpty(modelName) Then Dim modelDescriptionGenerator As ModelDescriptionGenerator = Configuration.GetModelDescriptionGenerator() Dim modelDescription As ModelDescription = Nothing If modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, modelDescription) Then Return View(modelDescription) End If End If Return View(ErrorViewName) End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/HelpPage.css ================================================ .help-page h1, .help-page .h1, .help-page h2, .help-page .h2, .help-page h3, .help-page .h3, #body.help-page, .help-page-table th, .help-page-table pre, .help-page-table p { font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; } .help-page pre.wrapped { white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; } .help-page .warning-message-container { margin-top: 20px; padding: 0 10px; color: #525252; background: #EFDCA9; border: 1px solid #CCCCCC; } .help-page-table { width: 100%; border-collapse: collapse; text-align: left; margin: 0px 0px 20px 0px; border-top: 1px solid #D4D4D4; } .help-page-table th { text-align: left; font-weight: bold; border-bottom: 1px solid #D4D4D4; padding: 5px 6px 5px 6px; } .help-page-table td { border-bottom: 1px solid #D4D4D4; padding: 10px 8px 10px 8px; vertical-align: top; } .help-page-table pre, .help-page-table p { margin: 0px; padding: 0px; font-family: inherit; font-size: 100%; } .help-page-table tbody tr:hover td { background-color: #F3F3F3; } .help-page a:hover { background-color: transparent; } .help-page .sample-header { border: 2px solid #D4D4D4; background: #00497E; color: #FFFFFF; padding: 8px 15px; border-bottom: none; display: inline-block; margin: 10px 0px 0px 0px; } .help-page .sample-content { display: block; border-width: 0; padding: 15px 20px; background: #FFFFFF; border: 2px solid #D4D4D4; margin: 0px 0px 10px 0px; } .help-page .api-name { width: 40%; } .help-page .api-documentation { width: 60%; } .help-page .parameter-name { width: 20%; } .help-page .parameter-documentation { width: 40%; } .help-page .parameter-type { width: 20%; } .help-page .parameter-annotations { width: 20%; } .help-page h1, .help-page .h1 { font-size: 36px; line-height: normal; } .help-page h2, .help-page .h2 { font-size: 24px; } .help-page h3, .help-page .h3 { font-size: 20px; } #body.help-page { font-size: 14px; line-height: 143%; color: #333; } .help-page a { color: #0000EE; text-decoration: none; } ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/HelpPageAreaRegistration.vb ================================================ Imports System.Web.Http Imports System.Web.Mvc Namespace Areas.HelpPage Public Class HelpPageAreaRegistration Inherits AreaRegistration Public Overrides ReadOnly Property AreaName As String Get Return "HelpPage" End Get End Property Public Overrides Sub RegisterArea(context As AreaRegistrationContext) context.MapRoute( "HelpPage_Default", "Help/{action}/{apiId}", New With {.Controller = "Help", .action = "Index", .apiId = UrlParameter.Optional}) HelpPageConfig.Register(GlobalConfiguration.Configuration) End Sub End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/HelpPageConfigurationExtensions.vb ================================================ Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.ComponentModel Imports System.Diagnostics Imports System.Diagnostics.CodeAnalysis Imports System.Globalization Imports System.Linq Imports System.Net.Http Imports System.Net.Http.Headers Imports System.Runtime.CompilerServices Imports System.Web.Http Imports System.Web.Http.Controllers Imports System.Web.Http.Description Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage Public Module HelpPageConfigurationExtensions Private Const ApiModelPrefix As String = "MS_HelpPageApiModel_" ''' ''' Sets the documentation provider for help page. ''' ''' The . ''' The documentation provider. Public Sub SetDocumentationProvider(ByVal config As HttpConfiguration, documentationProvider As IDocumentationProvider) config.Services.Replace(GetType(IDocumentationProvider), documentationProvider) End Sub ''' ''' Sets the objects that will be used by the formatters to produce sample requests/responses. ''' ''' The . ''' The sample objects. Public Sub SetSampleObjects(ByVal config As HttpConfiguration, sampleObjects As IDictionary(Of Type, Object)) config.GetHelpPageSampleGenerator().SampleObjects = sampleObjects End Sub ''' ''' Sets the sample request directly for the specified media type and action. ''' ''' The . ''' The sample request. ''' The media type. ''' Name of the controller. ''' Name of the action. Public Sub SetSampleRequest(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, New String() {"*"}), sample) End Sub ''' ''' Sets the sample request directly for the specified media type and action with parameters. ''' ''' The . ''' The sample request. ''' The media type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetSampleRequest(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Request, controllerName, actionName, parameterNames), sample) End Sub ''' ''' Sets the sample request directly for the specified media type of the action. ''' ''' The . ''' The sample response. ''' The media type. ''' Name of the controller. ''' Name of the action. Public Sub SetSampleResponse(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, New String() {"*"}), sample) End Sub ''' ''' Sets the sample response directly for the specified media type of the action with specific parameters. ''' ''' The . ''' The sample response. ''' The media type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetSampleResponse(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, SampleDirection.Response, controllerName, actionName, parameterNames), sample) End Sub ''' ''' Sets the sample directly for all actions with the specified type. ''' ''' The . ''' The sample. ''' The media type. Public Sub SetSampleForMediaType(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType), sample) End Sub ''' ''' Sets the sample directly for all actions with the specified type and media type. ''' ''' The . ''' The sample. ''' The media type. ''' The parameter type or return type of an action. Public Sub SetSampleForType(ByVal config As HttpConfiguration, sample As Object, mediaType As MediaTypeHeaderValue, type As Type) config.GetHelpPageSampleGenerator().ActionSamples.Add(New HelpPageSampleKey(mediaType, type), sample) End Sub ''' ''' Specifies the actual type of passed to the in an action. ''' The help page will use this information to produce more accurate request samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. Public Sub SetActualRequestType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, New String() {"*"}), type) End Sub ''' ''' Specifies the actual type of passed to the in an action. ''' The help page will use this information to produce more accurate request samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetActualRequestType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Request, controllerName, actionName, parameterNames), type) End Sub ''' ''' Specifies the actual type of returned as part of the in an action. ''' The help page will use this information to produce more accurate response samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. Public Sub SetActualResponseType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, New String() {"*"}), type) End Sub ''' ''' Specifies the actual type of returned as part of the in an action. ''' The help page will use this information to produce more accurate response samples. ''' ''' The . ''' The type. ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub SetActualResponseType(ByVal config As HttpConfiguration, type As Type, controllerName As String, actionName As String, ByVal ParamArray parameterNames() As String) config.GetHelpPageSampleGenerator().ActualHttpMessageTypes.Add(New HelpPageSampleKey(SampleDirection.Response, controllerName, actionName, parameterNames), type) End Sub ''' ''' Gets the help page sample generator. ''' ''' The . ''' The help page sample generator. Public Function GetHelpPageSampleGenerator(ByVal config As HttpConfiguration) As HelpPageSampleGenerator Return DirectCast(config.Properties.GetOrAdd( GetType(HelpPageSampleGenerator), Function(k) New HelpPageSampleGenerator()), HelpPageSampleGenerator) End Function ''' ''' Gets the model description generator. ''' ''' The configuration. ''' The Public Function GetModelDescriptionGenerator(config As HttpConfiguration) As ModelDescriptionGenerator Return DirectCast(config.Properties.GetOrAdd(GetType(ModelDescriptionGenerator), Function(k) InitializeModelDescriptionGenerator(config)), ModelDescriptionGenerator) End Function ''' ''' Sets the help page sample generator. ''' ''' The . ''' The help page sample generator. Public Sub SetHelpPageSampleGenerator(ByVal config As HttpConfiguration, sampleGenerator As HelpPageSampleGenerator) config.Properties.AddOrUpdate( GetType(HelpPageSampleGenerator), Function(k) sampleGenerator, Function(k, o) sampleGenerator) End Sub ''' ''' Gets the model that represents an API displayed on the help page. The model is initialized on the first call and cached for subsequent calls. ''' ''' The . ''' The ID. ''' ''' An ''' Public Function GetHelpPageApiModel(ByVal config As HttpConfiguration, apiDescriptionId As String) As HelpPageApiModel Dim model As New Object Dim modelId As String = ApiModelPrefix + apiDescriptionId If (Not config.Properties.TryGetValue(modelId, model)) Then Dim apiDescriptions As Collection(Of ApiDescription) = config.Services.GetApiExplorer().ApiDescriptions Dim ApiDescription As ApiDescription = apiDescriptions.FirstOrDefault(Function(api) String.Equals(api.GetFriendlyId(), apiDescriptionId, StringComparison.OrdinalIgnoreCase)) If (Not ApiDescription Is Nothing) Then model = GenerateApiModel(ApiDescription, config) config.Properties.TryAdd(modelId, model) End If End If Return DirectCast(model, HelpPageApiModel) End Function Public Function GenerateApiModel(apiDescription As ApiDescription, config As HttpConfiguration) As HelpPageApiModel Dim apiModel As New HelpPageApiModel() With { .ApiDescription = apiDescription } Dim sampleGenerator As HelpPageSampleGenerator = config.GetHelpPageSampleGenerator() Dim modelGenerator As ModelDescriptionGenerator = config.GetModelDescriptionGenerator() GenerateUriParameters(apiModel, modelGenerator) GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator) GenerateResourceDescription(apiModel, modelGenerator) GenerateSamples(apiModel, sampleGenerator) Return apiModel End Function Private Sub GenerateUriParameters(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator) Dim apiDescription As ApiDescription = apiModel.ApiDescription For Each apiParameter As ApiParameterDescription In apiDescription.ParameterDescriptions If apiParameter.Source = ApiParameterSource.FromUri Then Dim parameterDescriptor As HttpParameterDescriptor = apiParameter.ParameterDescriptor Dim parameterType As Type = Nothing Dim typeDescription As ModelDescription = Nothing Dim complexTypeDescription As ComplexTypeModelDescription = Nothing If parameterDescriptor IsNot Nothing Then parameterType = parameterDescriptor.ParameterType typeDescription = modelGenerator.GetOrCreateModelDescription(parameterType) complexTypeDescription = TryCast(typeDescription, ComplexTypeModelDescription) End If '' Example: '' '' Public Class Point '' Public Sub New(x As Integer, y As Integer) '' x = x '' y = y '' End Sub '' Public X As Integer '' Public Y As Integer '' End Class '' Class Point is bindable with a TypeConverter, so Point will be added to UriParameters collection. '' '' Public Class Point '' Public X As Integer '' Public Y As Integer '' End Class '' Regular complex class Point will have properties X and Y added to UriParameters collection. If complexTypeDescription IsNot Nothing AndAlso Not IsBindableWithTypeConverter(parameterType) Then For Each uriParameter As ParameterDescription In complexTypeDescription.Properties apiModel.UriParameters.Add(uriParameter) Next ElseIf parameterDescriptor IsNot Nothing Then Dim uriParameter As ParameterDescription = AddParameterDescription(apiModel, apiParameter, typeDescription) If Not parameterDescriptor.IsOptional Then uriParameter.Annotations.Add(New ParameterAnnotation() With { .Documentation = "Required" }) End If Dim defaultValue As Object = parameterDescriptor.DefaultValue If defaultValue IsNot Nothing Then uriParameter.Annotations.Add(New ParameterAnnotation() With { .Documentation = "Default value is " & Convert.ToString(defaultValue, CultureInfo.InvariantCulture) }) End If Else Debug.Assert(parameterDescriptor Is Nothing) '' If parameterDescriptor is Nothing, this is an undeclared route parameter which only occurs '' when source is FromUri. Ignored in request model and among resource parameters but listed '' as a simple string here. Dim modelDescription As ModelDescription = modelGenerator.GetOrCreateModelDescription(GetType(String)) AddParameterDescription(apiModel, apiParameter, modelDescription) End If End If Next End Sub Private Function IsBindableWithTypeConverter(type As Type) As Boolean If type Is Nothing Then Return False End If Return TypeDescriptor.GetConverter(type).CanConvertFrom(GetType(String)) End Function Private Function AddParameterDescription(apiModel As HelpPageApiModel, apiParameter As ApiParameterDescription, typeDescription As ModelDescription) As ParameterDescription Dim parameterDescription As New ParameterDescription() With { .Name = apiParameter.Name, .Documentation = apiParameter.Documentation, .TypeDescription = typeDescription } apiModel.UriParameters.Add(parameterDescription) Return parameterDescription End Function Private Sub GenerateRequestModelDescription(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator, sampleGenerator As HelpPageSampleGenerator) Dim apiDescription As ApiDescription = apiModel.ApiDescription For Each apiParameter As ApiParameterDescription In apiDescription.ParameterDescriptions If apiParameter.Source = ApiParameterSource.FromBody Then Dim parameterType As Type = apiParameter.ParameterDescriptor.ParameterType apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType) apiModel.RequestDocumentation = apiParameter.Documentation ElseIf apiParameter.ParameterDescriptor IsNot Nothing AndAlso apiParameter.ParameterDescriptor.ParameterType = GetType(HttpRequestMessage) Then Dim parameterType As Type = sampleGenerator.ResolveHttpRequestMessageType(apiDescription) If parameterType IsNot Nothing Then apiModel.RequestModelDescription = modelGenerator.GetOrCreateModelDescription(parameterType) End If End If Next End Sub Private Sub GenerateResourceDescription(apiModel As HelpPageApiModel, modelGenerator As ModelDescriptionGenerator) Dim response As ResponseDescription = apiModel.ApiDescription.ResponseDescription Dim responseType As Type = If(response.ResponseType, response.DeclaredType) If responseType IsNot Nothing AndAlso responseType <> GetType(System.Void) Then apiModel.ResourceDescription = modelGenerator.GetOrCreateModelDescription(responseType) End If End Sub Private Sub GenerateSamples(apiModel As HelpPageApiModel, sampleGenerator As HelpPageSampleGenerator) Try For Each item In sampleGenerator.GetSampleRequests(apiModel.ApiDescription) apiModel.SampleRequests.Add(item.Key, item.Value) LogInvalidSampleAsError(apiModel, item.Value) Next For Each item In sampleGenerator.GetSampleResponses(apiModel.ApiDescription) apiModel.SampleResponses.Add(item.Key, item.Value) LogInvalidSampleAsError(apiModel, item.Value) Next Catch e As Exception apiModel.ErrorMessages.Add(String.Format(CultureInfo.CurrentCulture, "An exception has occurred while generating the sample. Exception message: {0}", HelpPageSampleGenerator.UnwrapException(e).Message)) End Try End Sub Private Function TryGetResourceParameter(apiDescription As ApiDescription, config As HttpConfiguration, ByRef parameterDescription As ApiParameterDescription, ByRef resourceType As Type) As Boolean parameterDescription = apiDescription.ParameterDescriptions.FirstOrDefault( Function(p) p.Source = ApiParameterSource.FromBody OrElse (p.ParameterDescriptor IsNot Nothing AndAlso p.ParameterDescriptor.ParameterType = GetType(HttpRequestMessage))) If parameterDescription Is Nothing Then resourceType = Nothing Return False End If resourceType = parameterDescription.ParameterDescriptor.ParameterType If resourceType = GetType(HttpRequestMessage) Then Dim sampleGenerator As HelpPageSampleGenerator = config.GetHelpPageSampleGenerator() resourceType = sampleGenerator.ResolveHttpRequestMessageType(apiDescription) End If If resourceType Is Nothing Then parameterDescription = Nothing Return False End If Return True End Function Private Function InitializeModelDescriptionGenerator(config As HttpConfiguration) As ModelDescriptionGenerator Dim modelGenerator As New ModelDescriptionGenerator(config) Dim apis As Collection(Of ApiDescription) = config.Services.GetApiExplorer().ApiDescriptions For Each api As ApiDescription In apis Dim parameterDescription As ApiParameterDescription = Nothing Dim parameterType As Type = Nothing If TryGetResourceParameter(api, config, parameterDescription, parameterType) Then modelGenerator.GetOrCreateModelDescription(parameterType) End If Next Return modelGenerator End Function Private Sub LogInvalidSampleAsError(apiModel As HelpPageApiModel, sample As Object) Dim invalidSample As InvalidSample = TryCast(sample, InvalidSample) If (Not invalidSample Is Nothing) Then apiModel.ErrorMessages.Add(invalidSample.ErrorMessage) End If End Sub End Module End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.vb ================================================ Namespace Areas.HelpPage.ModelDescriptions Public Class CollectionModelDescription Inherits ModelDescription Private _elementDescription As ModelDescription Public Property ElementDescription() As ModelDescription Get Return _elementDescription End Get Set(value As ModelDescription) _elementDescription = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.vb ================================================ Imports System.Collections.ObjectModel Namespace Areas.HelpPage.ModelDescriptions Public Class ComplexTypeModelDescription Inherits ModelDescription Private _properties As Collection(Of ParameterDescription) Public Sub New() Properties = New Collection(Of ParameterDescription)() End Sub Public Property Properties() As Collection(Of ParameterDescription) Get Return _properties End Get Private Set(value As Collection(Of ParameterDescription)) _properties = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.vb ================================================ Namespace Areas.HelpPage.ModelDescriptions Public Class DictionaryModelDescription Inherits KeyValuePairModelDescription End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.vb ================================================ Imports System.Collections.Generic Imports System.Collections.ObjectModel Namespace Areas.HelpPage.ModelDescriptions Public Class EnumTypeModelDescription Inherits ModelDescription Private _values As Collection(Of EnumValueDescription) Public Sub New() Values = New Collection(Of EnumValueDescription)() End Sub Public Property Values() As Collection(Of EnumValueDescription) Get Return _values End Get Private Set(value As Collection(Of EnumValueDescription)) _values = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/EnumValueDescription.vb ================================================ Namespace Areas.HelpPage.ModelDescriptions Public Class EnumValueDescription Private _documentation As String Private _value As String Private _name As String Public Property Documentation() As String Get Return _documentation End Get Set(value As String) _documentation = value End Set End Property Public Property Name() As String Get Return _name End Get Set(value As String) _name = value End Set End Property Public Property Value() As String Get Return _value End Get Set(value As String) _value = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.vb ================================================ Imports System Imports System.Reflection Namespace Areas.HelpPage.ModelDescriptions Public Interface IModelDocumentationProvider Function GetDocumentation(member As MemberInfo) As String Function GetDocumentation(type As Type) As String End Interface End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.vb ================================================ Namespace Areas.HelpPage.ModelDescriptions Public Class KeyValuePairModelDescription Inherits ModelDescription Private _keyModelDescription As ModelDescription Private _valueModelDescription As ModelDescription Public Property KeyModelDescription() As ModelDescription Get Return _keyModelDescription End Get Set(value As ModelDescription) _keyModelDescription = value End Set End Property Public Property ValueModelDescription() As ModelDescription Get Return _valueModelDescription End Get Set(value As ModelDescription) _valueModelDescription = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ModelDescription.vb ================================================ Imports System Namespace Areas.HelpPage.ModelDescriptions ''' ''' Describes a type model. ''' Public MustInherit Class ModelDescription Private _name As String Private _documentation As String Private _modelType As Type Public Property Documentation() As String Get Return _documentation End Get Set(value As String) _documentation = value End Set End Property Public Property ModelType() As Type Get Return _modelType End Get Set(value As Type) _modelType = value End Set End Property Public Property Name() As String Get Return _name End Get Set(value As String) _name = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ModelDescriptionGenerator.vb ================================================ Imports System Imports System.Collections Imports System.Collections.Generic Imports System.Collections.Specialized Imports System.ComponentModel.DataAnnotations Imports System.Globalization Imports System.Reflection Imports System.Runtime.Serialization Imports System.Web.Http Imports System.Web.Http.Description Imports System.Xml.Serialization Imports Newtonsoft.Json Namespace Areas.HelpPage.ModelDescriptions ''' ''' Generates model descriptions for given types. ''' Public Class ModelDescriptionGenerator ' Modify this to support more data annotation attributes. Private ReadOnly AnnotationTextGenerator As IDictionary(Of Type, Func(Of Object, String)) = New Dictionary(Of Type, Func(Of Object, String))() From { _ {GetType(RequiredAttribute), Function(a) "Required"}, {GetType(RangeAttribute), Function(a) Dim range As RangeAttribute = DirectCast(a, RangeAttribute) Return [String].Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum) End Function}, {GetType(MaxLengthAttribute), Function(a) Dim maxLength As MaxLengthAttribute = DirectCast(a, MaxLengthAttribute) Return [String].Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length) End Function}, {GetType(MinLengthAttribute), Function(a) Dim minLength As MinLengthAttribute = DirectCast(a, MinLengthAttribute) Return [String].Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length) End Function}, {GetType(StringLengthAttribute), Function(a) Dim strLength As StringLengthAttribute = DirectCast(a, StringLengthAttribute) Return [String].Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength) End Function}, {GetType(DataTypeAttribute), Function(a) Dim dataType As DataTypeAttribute = DirectCast(a, DataTypeAttribute) Return [String].Format(CultureInfo.CurrentCulture, "Data type: {0}", If(dataType.CustomDataType, dataType.DataType.ToString())) End Function}, {GetType(RegularExpressionAttribute), Function(a) Dim regularExpression As RegularExpressionAttribute = DirectCast(a, RegularExpressionAttribute) Return [String].Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern) End Function} } ' Modify this to add more default documentations. Private ReadOnly DefaultTypeDocumentation As IDictionary(Of Type, String) = New Dictionary(Of Type, String)() From { _ {GetType(Int16), "integer"}, {GetType(Int32), "integer"}, {GetType(Int64), "integer"}, {GetType(UInt16), "unsigned integer"}, {GetType(UInt32), "unsigned integer"}, {GetType(UInt64), "unsigned integer"}, {GetType([Byte]), "byte"}, {GetType([Char]), "character"}, {GetType([SByte]), "signed byte"}, {GetType(Uri), "URI"}, {GetType([Single]), "decimal number"}, {GetType([Double]), "decimal number"}, {GetType([Decimal]), "decimal number"}, {GetType([String]), "string"}, {GetType(Guid), "globally unique identifier"}, {GetType(TimeSpan), "time interval"}, {GetType(DateTime), "date"}, {GetType(DateTimeOffset), "date"}, {GetType([Boolean]), "boolean"} } Private _documentationProvider As Lazy(Of IModelDocumentationProvider) Public Sub New(config As HttpConfiguration) If config Is Nothing Then Throw New ArgumentNullException("config") End If _documentationProvider = New Lazy(Of IModelDocumentationProvider)(Function() TryCast(config.Services.GetDocumentationProvider(), IModelDocumentationProvider)) GeneratedModels = New Dictionary(Of String, ModelDescription)(StringComparer.OrdinalIgnoreCase) End Sub Public Property GeneratedModels() As Dictionary(Of String, ModelDescription) Get Return m_GeneratedModels End Get Private Set(value As Dictionary(Of String, ModelDescription)) m_GeneratedModels = value End Set End Property Private m_GeneratedModels As Dictionary(Of String, ModelDescription) Private ReadOnly Property DocumentationProvider() As IModelDocumentationProvider Get Return _documentationProvider.Value End Get End Property Public Function GetOrCreateModelDescription(modelType As Type) As ModelDescription If modelType Is Nothing Then Throw New ArgumentNullException("modelType") End If Dim underlyingType As Type = Nullable.GetUnderlyingType(modelType) If underlyingType IsNot Nothing Then modelType = underlyingType End If Dim modelDescription As ModelDescription = Nothing Dim modelName As String = ModelNameHelper.GetModelName(modelType) If GeneratedModels.TryGetValue(modelName, modelDescription) Then If modelType <> modelDescription.ModelType Then Throw New InvalidOperationException([String].Format(CultureInfo.CurrentCulture, "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " & "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.", modelName, modelDescription.ModelType.FullName, modelType.FullName)) End If Return modelDescription End If If DefaultTypeDocumentation.ContainsKey(modelType) Then Return GenerateSimpleTypeModelDescription(modelType) End If If modelType.IsEnum Then Return GenerateEnumTypeModelDescription(modelType) End If If modelType.IsGenericType Then Dim genericArguments As Type() = modelType.GetGenericArguments() If genericArguments.Length = 1 Then Dim enumerableType As Type = GetType(IEnumerable(Of )).MakeGenericType(genericArguments) If enumerableType.IsAssignableFrom(modelType) Then Return GenerateCollectionModelDescription(modelType, genericArguments(0)) End If End If If genericArguments.Length = 2 Then Dim dictionaryType As Type = GetType(IDictionary(Of ,)).MakeGenericType(genericArguments) If dictionaryType.IsAssignableFrom(modelType) Then Return GenerateDictionaryModelDescription(modelType, genericArguments(0), genericArguments(1)) End If Dim keyValuePairType As Type = GetType(KeyValuePair(Of ,)).MakeGenericType(genericArguments) If keyValuePairType.IsAssignableFrom(modelType) Then Return GenerateKeyValuePairModelDescription(modelType, genericArguments(0), genericArguments(1)) End If End If End If If modelType.IsArray Then Dim elementType As Type = modelType.GetElementType() Return GenerateCollectionModelDescription(modelType, elementType) End If If modelType Is GetType(NameValueCollection) Then Return GenerateDictionaryModelDescription(modelType, GetType(String), GetType(String)) End If If GetType(IDictionary).IsAssignableFrom(modelType) Then Return GenerateDictionaryModelDescription(modelType, GetType(Object), GetType(Object)) End If If GetType(IEnumerable).IsAssignableFrom(modelType) Then Return GenerateCollectionModelDescription(modelType, GetType(Object)) End If Return GenerateComplexTypeModelDescription(modelType) End Function ' Change this to provide different name for the member. Private Shared Function GetMemberName(member As MemberInfo, hasDataContractAttribute As Boolean) As String Dim jsonProperty As JsonPropertyAttribute = member.GetCustomAttribute(Of JsonPropertyAttribute)() If jsonProperty IsNot Nothing AndAlso Not [String].IsNullOrEmpty(jsonProperty.PropertyName) Then Return jsonProperty.PropertyName End If If hasDataContractAttribute Then Dim dataMember As DataMemberAttribute = member.GetCustomAttribute(Of DataMemberAttribute)() If dataMember IsNot Nothing AndAlso Not [String].IsNullOrEmpty(dataMember.Name) Then Return dataMember.Name End If End If Return member.Name End Function Private Shared Function ShouldDisplayMember(member As MemberInfo, hasDataContractAttribute As Boolean) As Boolean Dim jsonIgnore As JsonIgnoreAttribute = member.GetCustomAttribute(Of JsonIgnoreAttribute)() Dim xmlIgnore As XmlIgnoreAttribute = member.GetCustomAttribute(Of XmlIgnoreAttribute)() Dim ignoreDataMember As IgnoreDataMemberAttribute = member.GetCustomAttribute(Of IgnoreDataMemberAttribute)() Dim nonSerialized As NonSerializedAttribute = member.GetCustomAttribute(Of NonSerializedAttribute)() Dim apiExplorerSetting As ApiExplorerSettingsAttribute = member.GetCustomAttribute(Of ApiExplorerSettingsAttribute)() Dim hasMemberAttribute As Boolean = If(member.DeclaringType.IsEnum, member.GetCustomAttribute(Of EnumMemberAttribute)() IsNot Nothing, member.GetCustomAttribute(Of DataMemberAttribute)() IsNot Nothing) ' Display member only if all the followings are true: ' no JsonIgnoreAttribute ' no XmlIgnoreAttribute ' no IgnoreDataMemberAttribute ' no NonSerializedAttribute ' no ApiExplorerSettingsAttribute with IgnoreApi set to true ' no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute Return jsonIgnore Is Nothing AndAlso xmlIgnore Is Nothing AndAlso ignoreDataMember Is Nothing AndAlso nonSerialized Is Nothing AndAlso (apiExplorerSetting Is Nothing OrElse Not apiExplorerSetting.IgnoreApi) AndAlso (Not hasDataContractAttribute OrElse hasMemberAttribute) End Function Private Function CreateDefaultDocumentation(type As Type) As String Dim documentation As String = Nothing If DefaultTypeDocumentation.TryGetValue(type, documentation) Then Return documentation End If If DocumentationProvider IsNot Nothing Then documentation = DocumentationProvider.GetDocumentation(type) End If Return documentation End Function Private Sub GenerateAnnotations([property] As MemberInfo, propertyModel As ParameterDescription) Dim annotations As New List(Of ParameterAnnotation)() Dim attributes As IEnumerable(Of Attribute) = [property].GetCustomAttributes() For Each attribute As Attribute In attributes Dim textGenerator As Func(Of Object, String) = Nothing If AnnotationTextGenerator.TryGetValue(attribute.[GetType](), textGenerator) Then annotations.Add( New ParameterAnnotation() With { .AnnotationAttribute = attribute, .Documentation = textGenerator(attribute) }) End If Next ' Rearrange the annotations annotations.Sort( Function(x, y) ' Special-case RequiredAttribute so that it shows up on top If TypeOf x.AnnotationAttribute Is RequiredAttribute Then Return -1 End If If TypeOf y.AnnotationAttribute Is RequiredAttribute Then Return 1 End If ' Sort the rest based on alphabetic order of the documentation Return [String].Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase) End Function) For Each annotation As ParameterAnnotation In annotations propertyModel.Annotations.Add(annotation) Next End Sub Private Function GenerateCollectionModelDescription(modelType As Type, elementType As Type) As CollectionModelDescription Dim collectionModelDescription As ModelDescription = GetOrCreateModelDescription(elementType) If collectionModelDescription IsNot Nothing Then Return New CollectionModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .ElementDescription = collectionModelDescription } End If Return Nothing End Function Private Function GenerateComplexTypeModelDescription(modelType As Type) As ModelDescription Dim complexModelDescription As New ComplexTypeModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .Documentation = CreateDefaultDocumentation(modelType) } GeneratedModels.Add(complexModelDescription.Name, complexModelDescription) Dim hasDataContractAttribute As Boolean = modelType.GetCustomAttribute(Of DataContractAttribute)() IsNot Nothing Dim properties As PropertyInfo() = modelType.GetProperties(BindingFlags.[Public] Or BindingFlags.Instance) For Each [property] As PropertyInfo In properties If ShouldDisplayMember([property], hasDataContractAttribute) Then Dim propertyModel As New ParameterDescription() With { .Name = GetMemberName([property], hasDataContractAttribute) } If DocumentationProvider IsNot Nothing Then propertyModel.Documentation = DocumentationProvider.GetDocumentation([property]) End If GenerateAnnotations([property], propertyModel) complexModelDescription.Properties.Add(propertyModel) propertyModel.TypeDescription = GetOrCreateModelDescription([property].PropertyType) End If Next Dim fields As FieldInfo() = modelType.GetFields(BindingFlags.[Public] Or BindingFlags.Instance) For Each field As FieldInfo In fields If ShouldDisplayMember(field, hasDataContractAttribute) Then Dim propertyModel As New ParameterDescription() With { .Name = GetMemberName(field, hasDataContractAttribute) } If DocumentationProvider IsNot Nothing Then propertyModel.Documentation = DocumentationProvider.GetDocumentation(field) End If complexModelDescription.Properties.Add(propertyModel) propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType) End If Next Return complexModelDescription End Function Private Function GenerateDictionaryModelDescription(modelType As Type, keyType As Type, valueType As Type) As DictionaryModelDescription Dim keyModelDescription As ModelDescription = GetOrCreateModelDescription(keyType) Dim valueModelDescription As ModelDescription = GetOrCreateModelDescription(valueType) Return New DictionaryModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .KeyModelDescription = keyModelDescription, .ValueModelDescription = valueModelDescription } End Function Private Function GenerateEnumTypeModelDescription(modelType As Type) As EnumTypeModelDescription Dim enumDescription As New EnumTypeModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .Documentation = CreateDefaultDocumentation(modelType) } Dim hasDataContractAttribute As Boolean = modelType.GetCustomAttribute(Of DataContractAttribute)() IsNot Nothing For Each field As FieldInfo In modelType.GetFields(BindingFlags.[Public] Or BindingFlags.[Static]) If ShouldDisplayMember(field, hasDataContractAttribute) Then Dim enumValue As New EnumValueDescription() With { .Name = field.Name, .Value = field.GetRawConstantValue().ToString() } If DocumentationProvider IsNot Nothing Then enumValue.Documentation = DocumentationProvider.GetDocumentation(field) End If enumDescription.Values.Add(enumValue) End If Next GeneratedModels.Add(enumDescription.Name, enumDescription) Return enumDescription End Function Private Function GenerateKeyValuePairModelDescription(modelType As Type, keyType As Type, valueType As Type) As KeyValuePairModelDescription Dim keyModelDescription As ModelDescription = GetOrCreateModelDescription(keyType) Dim valueModelDescription As ModelDescription = GetOrCreateModelDescription(valueType) Return New KeyValuePairModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .KeyModelDescription = keyModelDescription, .ValueModelDescription = valueModelDescription } End Function Private Function GenerateSimpleTypeModelDescription(modelType As Type) As ModelDescription Dim simpleModelDescription As New SimpleTypeModelDescription() With { .Name = ModelNameHelper.GetModelName(modelType), .ModelType = modelType, .Documentation = CreateDefaultDocumentation(modelType) } GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription) Return simpleModelDescription End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.vb ================================================ Imports System Namespace Areas.HelpPage.ModelDescriptions ''' ''' Use this attribute to change the name of the generated for a type. ''' _ Public NotInheritable Class ModelNameAttribute Inherits Attribute Private _name As String Public Sub New(name As String) _name = name End Sub Public Property Name() As String Get Return _name End Get Private Set(value As String) _name = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ModelNameHelper.vb ================================================ Imports System Imports System.Globalization Imports System.Linq Imports System.Reflection Namespace Areas.HelpPage.ModelDescriptions Friend NotInheritable Class ModelNameHelper Private Sub New() End Sub ' Modify this to provide custom model name mapping. Public Shared Function GetModelName(type As Type) As String Dim modelNameAttribute As ModelNameAttribute = type.GetCustomAttribute(Of ModelNameAttribute)() If modelNameAttribute IsNot Nothing AndAlso Not [String].IsNullOrEmpty(modelNameAttribute.Name) Then Return modelNameAttribute.Name End If Dim modelName As String = type.Name If type.IsGenericType Then ' Format the generic type name to something like: GenericOfAgurment1AndArgument2 Dim genericType As Type = type.GetGenericTypeDefinition() Dim genericArguments As Type() = type.GetGenericArguments() Dim genericTypeName As String = genericType.Name ' Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("`"c)) Dim argumentTypeNames As String() = genericArguments.[Select](Function(t) GetModelName(t)).ToArray() modelName = [String].Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, [String].Join("And", argumentTypeNames)) End If Return modelName End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.vb ================================================ Imports System Namespace Areas.HelpPage.ModelDescriptions Public Class ParameterAnnotation Private _annotationAttribute As Attribute Private _documentation As String Public Property AnnotationAttribute() As Attribute Get Return _annotationAttribute End Get Set(value As Attribute) _annotationAttribute = value End Set End Property Public Property Documentation() As String Get Return _documentation End Get Set(value As String) _documentation = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/ParameterDescription.vb ================================================ Imports System.Collections.Generic Imports System.Collections.ObjectModel Namespace Areas.HelpPage.ModelDescriptions Public Class ParameterDescription Private _annotations As Collection(Of ParameterAnnotation) Private _documentation As String Private _name As String Private _typeDescription As ModelDescription Public Sub New() Annotations = New Collection(Of ParameterAnnotation)() End Sub Public Property Annotations() As Collection(Of ParameterAnnotation) Get Return _annotations End Get Private Set(value As Collection(Of ParameterAnnotation)) _annotations = value End Set End Property Public Property Documentation() As String Get Return _documentation End Get Set(value As String) _documentation = value End Set End Property Public Property Name() As String Get Return _name End Get Set(value As String) _name = value End Set End Property Public Property TypeDescription() As ModelDescription Get Return _typeDescription End Get Set(value As ModelDescription) _typeDescription = value End Set End Property End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.vb ================================================ Namespace Areas.HelpPage.ModelDescriptions Public Class SimpleTypeModelDescription Inherits ModelDescription End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Models/HelpPageApiModel.vb ================================================ Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Net.Http.Headers Imports System.Web.Http.Description Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage.Models ''' ''' The model that represents an API displayed on the help page. ''' Public Class HelpPageApiModel Private _sampleRequests As IDictionary(Of MediaTypeHeaderValue, Object) Private _sampleResponses As IDictionary(Of MediaTypeHeaderValue, Object) Private _errorMessages As Collection(Of String) Private _apiDescription As ApiDescription Private _uriParameters As Collection(Of ParameterDescription) Private _requestModelDescription As ModelDescription Private _resourceDescription As ModelDescription Private _requestDocumentation As String ''' ''' Initializes a new instance of the class. ''' Public Sub New() UriParameters = New Collection(Of ParameterDescription) SampleRequests = New Dictionary(Of MediaTypeHeaderValue, Object) SampleResponses = New Dictionary(Of MediaTypeHeaderValue, Object) ErrorMessages = New Collection(Of String) End Sub ''' ''' Gets or sets the that describes the API. ''' Public Property ApiDescription As ApiDescription Get Return _apiDescription End Get Set(value As ApiDescription) _apiDescription = value End Set End Property ''' ''' Gets or sets the collection that describes the URI parameters for the API. ''' Public Property UriParameters() As Collection(Of ParameterDescription) Get Return _uriParameters End Get Private Set(value As Collection(Of ParameterDescription)) _uriParameters = value End Set End Property ''' ''' Gets or sets the documentation for the request. ''' Public Property RequestDocumentation() As String Get Return _requestDocumentation End Get Set(value As String) _requestDocumentation = value End Set End Property ''' ''' Gets or sets the model description of the request body. ''' Public Property RequestModelDescription() As ModelDescription Get Return _requestModelDescription End Get Set(value As ModelDescription) _requestModelDescription = value End Set End Property ''' ''' Gets the request body parameter descriptions. ''' Public ReadOnly Property RequestBodyParameters() As IList(Of ParameterDescription) Get Return GetParameterDescriptions(RequestModelDescription) End Get End Property ''' ''' Gets or sets the that describes the resource. ''' Public Property ResourceDescription() As ModelDescription Get Return _resourceDescription End Get Set(value As ModelDescription) _resourceDescription = value End Set End Property ''' ''' Gets the resource property descriptions. ''' Public ReadOnly Property ResourceProperties() As IList(Of ParameterDescription) Get Return GetParameterDescriptions(ResourceDescription) End Get End Property ''' ''' Gets the sample requests associated with the API. ''' Public Property SampleRequests As IDictionary(Of MediaTypeHeaderValue, Object) Get Return _sampleRequests End Get Private Set(value As IDictionary(Of MediaTypeHeaderValue, Object)) _sampleRequests = value End Set End Property ''' ''' Gets the sample responses associated with the API. ''' Public Property SampleResponses As IDictionary(Of MediaTypeHeaderValue, Object) Get Return _sampleResponses End Get Private Set(value As IDictionary(Of MediaTypeHeaderValue, Object)) _sampleResponses = value End Set End Property ''' ''' Gets the error messages associated with this model. ''' Public Property ErrorMessages As Collection(Of String) Get Return _errorMessages End Get Private Set(value As Collection(Of String)) _errorMessages = value End Set End Property Private Shared Function GetParameterDescriptions(modelDescription As ModelDescription) As IList(Of ParameterDescription) Dim complexTypeModelDescription As ComplexTypeModelDescription = TryCast(modelDescription, ComplexTypeModelDescription) If complexTypeModelDescription IsNot Nothing Then Return complexTypeModelDescription.Properties End If Dim collectionModelDescription As CollectionModelDescription = TryCast(modelDescription, CollectionModelDescription) If collectionModelDescription IsNot Nothing Then complexTypeModelDescription = TryCast(collectionModelDescription.ElementDescription, ComplexTypeModelDescription) If complexTypeModelDescription IsNot Nothing Then Return complexTypeModelDescription.Properties End If End If Return Nothing End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/HelpPageSampleGenerator.vb ================================================ Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.ComponentModel Imports System.Diagnostics.CodeAnalysis Imports System.Globalization Imports System.IO Imports System.Linq Imports System.Net.Http Imports System.Net.Http.Formatting Imports System.Net.Http.Headers Imports System.Runtime.InteropServices Imports System.Web.Http.Description Imports System.Xml.Linq Imports Newtonsoft.Json Namespace Areas.HelpPage ''' ''' This class will generate the samples for the help page. ''' Public Class HelpPageSampleGenerator Private _actualHttpMessageTypes As IDictionary(Of HelpPageSampleKey, Type) Private _actionSamples As IDictionary(Of HelpPageSampleKey, Object) Private _sampleObjects As IDictionary(Of Type, Object) Private _sampleObjectFactories As IList(Of Func(Of HelpPageSampleGenerator, Type, Object)) ''' ''' Initializes a new instance of the class. ''' Public Sub New() ActualHttpMessageTypes = New Dictionary(Of HelpPageSampleKey, Type) ActionSamples = New Dictionary(Of HelpPageSampleKey, Object) SampleObjects = New Dictionary(Of Type, Object) SampleObjectFactories = New List(Of Func(Of HelpPageSampleGenerator, Type, Object)) SampleObjectFactories.Add(AddressOf DefaultSampleObjectFactory) End Sub ''' ''' Gets CLR types that are used as the content of or . ''' Public Property ActualHttpMessageTypes As IDictionary(Of HelpPageSampleKey, Type) Get Return _actualHttpMessageTypes End Get Friend Set(value As IDictionary(Of HelpPageSampleKey, Type)) _actualHttpMessageTypes = value End Set End Property ''' ''' Gets the objects that are used directly as samples for certain actions. ''' Public Property ActionSamples As IDictionary(Of HelpPageSampleKey, Object) Get Return _actionSamples End Get Friend Set(value As IDictionary(Of HelpPageSampleKey, Object)) _actionSamples = value End Set End Property ''' ''' Gets the objects that are serialized as samples by the supported formatters. ''' Public Property SampleObjects As IDictionary(Of Type, Object) Get Return _sampleObjects End Get Friend Set(value As IDictionary(Of Type, Object)) _sampleObjects = value End Set End Property ''' ''' Gets factories for the objects that the supported formatters will serialize as samples. Processed in order, ''' stopping when the factory successfully returns a non- object. ''' ''' ''' Collection includes just initially. Use ''' SampleObjectFactories.Insert(0, func) to provide an override and ''' SampleObjectFactories.Add(func) to provide a fallback. Public Property SampleObjectFactories As IList(Of Func(Of HelpPageSampleGenerator, Type, Object)) Get Return _sampleObjectFactories End Get Private Set(value As IList(Of Func(Of HelpPageSampleGenerator, Type, Object))) _sampleObjectFactories = value End Set End Property ''' ''' Gets the request body samples for a given . ''' ''' The . ''' The samples keyed by media type. Public Function GetSampleRequests(api As ApiDescription) As IDictionary(Of MediaTypeHeaderValue, Object) Return GetSample(api, SampleDirection.Request) End Function ''' ''' Gets the response body samples for a given . ''' ''' The . ''' The samples keyed by media type. Public Function GetSampleResponses(api As ApiDescription) As IDictionary(Of MediaTypeHeaderValue, Object) Return GetSample(api, SampleDirection.Response) End Function ''' ''' Gets the request or response body samples. ''' ''' The . ''' The value indicating whether the sample is for a request or for a response. ''' The samples keyed by media type. Public Overridable Function GetSample(api As ApiDescription, sampleDirection As SampleDirection) As IDictionary(Of MediaTypeHeaderValue, Object) If (api Is Nothing) Then Throw New ArgumentNullException("api") End If Dim controllerName As String = api.ActionDescriptor.ControllerDescriptor.ControllerName Dim actionName As String = api.ActionDescriptor.ActionName Dim parameterNames As IEnumerable(Of String) = api.ParameterDescriptions.Select(Function(p) p.Name) Dim formatters As New Collection(Of MediaTypeFormatter) Dim type As Type = ResolveType(api, controllerName, actionName, parameterNames, sampleDirection, formatters) Dim samples As New Dictionary(Of MediaTypeHeaderValue, Object) ' Use the samples provided directly for actions Dim actionSamples = GetAllActionSamples(controllerName, actionName, parameterNames, sampleDirection) For Each actionSample In actionSamples samples.Add(actionSample.Key.MediaType, WrapSampleIfString(actionSample.Value)) Next ' Do the sample generation based on formatters only if an action doesn't return an HttpResponseMessage. ' Here we cannot rely on formatters because we don't know what's in the HttpResponseMessage, it might not even use formatters. If (Not type Is Nothing AndAlso Not GetType(HttpResponseMessage).IsAssignableFrom(type)) Then Dim sampleObject As Object = GetSampleObject(type) For Each formatter In formatters For Each mediaType As MediaTypeHeaderValue In formatter.SupportedMediaTypes If (Not samples.ContainsKey(mediaType)) Then Dim sample As Object = GetActionSample(controllerName, actionName, parameterNames, type, formatter, mediaType, sampleDirection) ' If no sample found, try generate sample using formatter and sample object If (sample Is Nothing And Not sampleObject Is Nothing) Then sample = WriteSampleObjectUsingFormatter(formatter, sampleObject, type, mediaType) End If samples.Add(mediaType, WrapSampleIfString(sample)) End If Next Next End If Return samples End Function ''' ''' Search for samples that are provided directly through . ''' ''' Name of the controller. ''' Name of the action. ''' The parameter names. ''' The CLR type. ''' The formatter. ''' The media type. ''' The value indicating whether the sample is for a request or for a response. ''' The sample that matches the parameters. Public Overridable Function GetActionSample(controllerName As String, actionName As String, parameterNames As IEnumerable(Of String), type As Type, formatter As MediaTypeFormatter, mediaType As MediaTypeHeaderValue, sampleDirection As SampleDirection) As Object Dim sample As New Object ' First, try to get the sample provided for the specified mediaType, sampleDirection, controllerName, actionName and parameterNames. ' If not found, try to get the sample provided for the specified mediaType, sampleDirection, controllerName and actionName regardless of the parameterNames. ' If still not found, try to get the sample provided for the specified mediaType and type. ' Finally, try to get the sample provided for the specified mediaType. If (ActionSamples.TryGetValue(New HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, parameterNames), sample) OrElse ActionSamples.TryGetValue(New HelpPageSampleKey(mediaType, sampleDirection, controllerName, actionName, New String() {"*"}), sample) OrElse ActionSamples.TryGetValue(New HelpPageSampleKey(mediaType, type), sample) OrElse ActionSamples.TryGetValue(New HelpPageSampleKey(mediaType), sample)) Then Return sample End If Return Nothing End Function ''' ''' Gets the sample object that will be serialized by the formatters. ''' First, it will look at the . If no sample object is found, it will try to create ''' one using (which wraps an ) and other ''' factories in . ''' ''' The type. ''' The sample object. Public Overridable Function GetSampleObject(type As Type) As Object Dim sampleObject As New Object If (Not SampleObjects.TryGetValue(type, sampleObject)) Then ' No specific object available, try our factories. For Each factory As Func(Of HelpPageSampleGenerator, Type, Object) In SampleObjectFactories If factory Is Nothing Then Continue For End If Try sampleObject = factory(Me, type) If sampleObject IsNot Nothing Then Exit For End If Catch ' Ignore any problems encountered in the factory; go on to the next one (if any). End Try Next End If Return sampleObject End Function ''' ''' Resolves the actual type of passed to the in an action. ''' ''' The . ''' The type. Public Overridable Function ResolveHttpRequestMessageType(api As ApiDescription) As Type Dim controllerName As String = api.ActionDescriptor.ControllerDescriptor.ControllerName Dim actionName As String = api.ActionDescriptor.ActionName Dim parameterNames As IEnumerable(Of String) = api.ParameterDescriptions.[Select](Function(p) p.Name) Dim formatters As Collection(Of MediaTypeFormatter) = Nothing Return ResolveType(api, controllerName, actionName, parameterNames, SampleDirection.Request, formatters) End Function ''' ''' Resolves the type of the action parameter or return value when or is used. ''' ''' The . ''' Name of the controller. ''' Name of the action. ''' The parameter names. ''' The value indicating whether the sample is for a request or a response. ''' The formatters. Public Overridable Function ResolveType(api As ApiDescription, controllerName As String, actionName As String, parameterNames As IEnumerable(Of String), sampleDirection As SampleDirection, ByRef formatters As Collection(Of MediaTypeFormatter)) As Type If (Not [Enum].IsDefined(GetType(SampleDirection), sampleDirection)) Then Throw New InvalidEnumArgumentException("sampleDirection", CInt(sampleDirection), GetType(SampleDirection)) End If If (api Is Nothing) Then Throw New ArgumentNullException("api") End If Dim type As Type = GetType(Object) If (ActualHttpMessageTypes.TryGetValue(New HelpPageSampleKey(sampleDirection, controllerName, actionName, parameterNames), type) OrElse ActualHttpMessageTypes.TryGetValue(New HelpPageSampleKey(sampleDirection, controllerName, actionName, New String() {"*"}), type)) Then ' Re-compute the supported formatters based on type Dim newFormatters As New Collection(Of MediaTypeFormatter) For Each formatter In api.ActionDescriptor.Configuration.Formatters If (IsFormatSupported(sampleDirection, formatter, type)) Then newFormatters.Add(formatter) End If Next formatters = newFormatters Else Select Case sampleDirection Case sampleDirection.Request Dim requestBodyParameter As ApiParameterDescription = api.ParameterDescriptions.FirstOrDefault(Function(p) p.Source = ApiParameterSource.FromBody) type = If(requestBodyParameter Is Nothing, Nothing, requestBodyParameter.ParameterDescriptor.ParameterType) formatters = api.SupportedRequestBodyFormatters Case Else 'Case sampleDirection.Response type = If(api.ResponseDescription.ResponseType, api.ResponseDescription.DeclaredType) formatters = api.SupportedResponseFormatters End Select End If Return type End Function ''' ''' Writes the sample object using formatter. ''' ''' The formatter. ''' The value. ''' The type. ''' Type of the media. ''' Public Overridable Function WriteSampleObjectUsingFormatter(formatter As MediaTypeFormatter, value As Object, type As Type, mediaType As MediaTypeHeaderValue) As Object If (formatter Is Nothing) Then Throw New ArgumentNullException("formatter") End If If (mediaType Is Nothing) Then Throw New ArgumentNullException("mediaType") End If Dim sample As Object = String.Empty Dim MS As MemoryStream = Nothing Dim content As HttpContent = Nothing Try If (formatter.CanWriteType(type)) Then MS = New MemoryStream() content = New ObjectContent(type, value, formatter, mediaType) formatter.WriteToStreamAsync(type, value, MS, content, Nothing).Wait() MS.Position = 0 Dim reader As New StreamReader(MS) Dim serializedSampleString As String = reader.ReadToEnd() If (mediaType.MediaType.ToUpperInvariant().Contains("XML")) Then serializedSampleString = TryFormatXml(serializedSampleString) ElseIf (mediaType.MediaType.ToUpperInvariant().Contains("JSON")) Then serializedSampleString = TryFormatJson(serializedSampleString) End If sample = New TextSample(serializedSampleString) Else sample = New InvalidSample(String.Format( CultureInfo.CurrentCulture, "Failed to generate the sample for media type '{0}'. Cannot use formatter '{1}' to write type '{2}'.", mediaType, formatter.GetType().Name, type.Name)) End If Catch e As Exception sample = New InvalidSample(String.Format( CultureInfo.CurrentCulture, "An exception has occurred while using the formatter '{0}' to generate sample for media type '{1}'. Exception message: {2}", formatter.GetType().Name, mediaType.MediaType, UnwrapException(e).Message)) Finally If (Not MS Is Nothing) Then MS.Dispose() End If If (Not content Is Nothing) Then content.Dispose() End If End Try Return sample End Function Friend Shared Function UnwrapException(exception As Exception) As Exception Dim aggregateException As AggregateException = TryCast(exception, AggregateException) If aggregateException IsNot Nothing Then Return aggregateException.Flatten().InnerException End If Return exception End Function Private Shared Function DefaultSampleObjectFactory(sampleGenerator As HelpPageSampleGenerator, type As Type) As Object ' Try create a default sample object Dim objectGenerator As New ObjectGenerator() Return objectGenerator.GenerateObject(type) End Function Private Shared Function TryFormatJson(str As String) As String Try Dim parsedJson As Object = JsonConvert.DeserializeObject(str) Return JsonConvert.SerializeObject(parsedJson, Formatting.Indented) Catch ' can't parse JSON, return the original string Return str End Try End Function Private Shared Function TryFormatXml(str As String) As String Try Dim Xml As XDocument = XDocument.Parse(str) Return Xml.ToString() Catch ' can't parse XML, return the original string Return str End Try End Function Private Shared Function IsFormatSupported(sampleDirection As SampleDirection, formatter As MediaTypeFormatter, type As Type) As Boolean Select Case sampleDirection Case sampleDirection.Request Return formatter.CanReadType(type) Case sampleDirection.Response Return formatter.CanWriteType(type) End Select Return False End Function Private Iterator Function GetAllActionSamples(controllerName As String, actionName As String, parameterNames As IEnumerable(Of String), sampleDirection As SampleDirection) As IEnumerable(Of KeyValuePair(Of HelpPageSampleKey, Object)) Dim parameterNamesSet As New HashSet(Of String)(parameterNames, StringComparer.OrdinalIgnoreCase) For Each sample In ActionSamples Dim sampleKey As HelpPageSampleKey = sample.Key If (String.Equals(controllerName, sampleKey.ControllerName, StringComparison.OrdinalIgnoreCase) And String.Equals(actionName, sampleKey.ActionName, StringComparison.OrdinalIgnoreCase) And (sampleKey.ParameterNames.SetEquals(New String() {"*"}) Or parameterNamesSet.SetEquals(sampleKey.ParameterNames)) And sampleDirection = sampleKey.SampleDirection) Then Yield sample End If Next End Function Private Shared Function WrapSampleIfString(sample As Object) As Object Dim stringSample As String = TryCast(sample, String) If (Not stringSample Is Nothing) Then Return New TextSample(stringSample) End If Return sample End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/HelpPageSampleKey.vb ================================================ Imports System Imports System.Collections.Generic Imports System.ComponentModel Imports System.Net.Http.Headers Namespace Areas.HelpPage ''' ''' This is used to identify the place where the sample should be applied. ''' Public Class HelpPageSampleKey Private _actionName As String Private _controllerName As String Private _mediaType As MediaTypeHeaderValue Private _parameterNames As HashSet(Of String) Private _parameterType As Type Private _sampleDirection As Nullable(Of SampleDirection) ''' ''' Creates a new based on media type. ''' ''' The media type. Public Sub New(mediaType As MediaTypeHeaderValue) If (mediaType Is Nothing) Then Throw New ArgumentNullException("mediaType") End If _actionName = String.Empty _controllerName = String.Empty _parameterNames = New HashSet(Of String)(StringComparer.OrdinalIgnoreCase) _mediaType = mediaType End Sub ''' ''' Creates a new based on media type and CLR type. ''' ''' The media type. ''' The CLR type. Public Sub New(mediaType As MediaTypeHeaderValue, type As Type) MyClass.New(mediaType) If (type Is Nothing) Then Throw New ArgumentNullException("type") End If _parameterType = type End Sub ''' ''' Creates a new based on , controller name, action name and parameter names. ''' ''' The . ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub New(sampleDirection As SampleDirection, controllerName As String, actionName As String, parameterNames As IEnumerable(Of String)) If (Not [Enum].IsDefined(GetType(SampleDirection), sampleDirection)) Then Throw New InvalidEnumArgumentException("sampleDirection", CInt(sampleDirection), GetType(SampleDirection)) End If If (controllerName Is Nothing) Then Throw New ArgumentNullException("controllerName") End If If (actionName Is Nothing) Then Throw New ArgumentNullException("actionName") End If If (parameterNames Is Nothing) Then Throw New ArgumentNullException("parameterNames") End If _controllerName = controllerName _actionName = actionName _parameterNames = New HashSet(Of String)(parameterNames, StringComparer.OrdinalIgnoreCase) _sampleDirection = sampleDirection End Sub ''' ''' Creates a new based on media type, , controller name, action name and parameter names. ''' ''' The media type. ''' The . ''' Name of the controller. ''' Name of the action. ''' The parameter names. Public Sub New(mediaType As MediaTypeHeaderValue, sampleDirection As SampleDirection, controllerName As String, actionName As String, parameterNames As IEnumerable(Of String)) MyClass.New(sampleDirection, controllerName, actionName, parameterNames) If (mediaType Is Nothing) Then Throw New ArgumentNullException("mediaType") End If _mediaType = mediaType End Sub ''' ''' Gets the name of the controller. ''' ''' ''' The name of the controller. ''' Public ReadOnly Property ControllerName As String Get Return _controllerName End Get End Property ''' ''' Gets the name of the action. ''' ''' ''' The name of the action. ''' Public ReadOnly Property ActionName As String Get Return _actionName End Get End Property ''' ''' Gets the media type. ''' ''' ''' The media type. ''' Public ReadOnly Property MediaType As MediaTypeHeaderValue Get Return _mediaType End Get End Property ''' ''' Gets the parameter names. ''' Public ReadOnly Property ParameterNames As HashSet(Of String) Get Return _parameterNames End Get End Property Public ReadOnly Property ParameterType As Type Get Return _parameterType End Get End Property ''' ''' Gets the . ''' Public ReadOnly Property SampleDirection As Nullable(Of SampleDirection) Get Return _sampleDirection End Get End Property Public Overrides Function Equals(obj As Object) As Boolean Dim otherKey As HelpPageSampleKey = TryCast(obj, HelpPageSampleKey) If (otherKey Is Nothing) Then Return False End If Return String.Equals(ControllerName, otherKey.ControllerName, StringComparison.OrdinalIgnoreCase) And String.Equals(ActionName, otherKey.ActionName, StringComparison.OrdinalIgnoreCase) And (MediaType Is otherKey.MediaType Or (Not MediaType Is Nothing AndAlso MediaType.Equals(otherKey.MediaType))) And ParameterType = otherKey.ParameterType And SampleDirection.Equals(otherKey.SampleDirection) And ParameterNames.SetEquals(otherKey.ParameterNames) End Function Public Overrides Function GetHashCode() As Integer Dim hashCode As Integer = ControllerName.ToUpperInvariant().GetHashCode() Xor ActionName.ToUpperInvariant().GetHashCode() If (Not MediaType Is Nothing) Then hashCode = hashCode Xor MediaType.GetHashCode() End If If (SampleDirection.HasValue) Then hashCode = hashCode Xor SampleDirection.GetHashCode() End If If (Not ParameterType Is Nothing) Then hashCode = hashCode Xor ParameterType.GetHashCode() End If For Each parameterName As String In ParameterNames hashCode = hashCode Xor parameterName.ToUpperInvariant().GetHashCode() Next Return hashCode End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/ImageSample.vb ================================================ Imports System Namespace Areas.HelpPage ''' ''' This represents an image sample on the help page. There's a display template named ImageSample associated with this class. ''' Public Class ImageSample Private _src As String ''' ''' Initializes a new instance of the class. ''' ''' The URL of an image. Public Sub New(src As String) If (src Is Nothing) Then Throw New ArgumentNullException("src") End If Me.Src = src End Sub Public Property Src As String Get Return _src End Get Private Set(value As String) _src = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean Dim other As ImageSample = TryCast(obj, ImageSample) Return Not other Is Nothing AndAlso Src = other.Src End Function Public Overrides Function GetHashCode() As Integer Return Src.GetHashCode() End Function Public Overrides Function ToString() As String Return Src End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/InvalidSample.vb ================================================ Imports System Namespace Areas.HelpPage ''' ''' This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. ''' Public Class InvalidSample Private _errorMessage As String Public Sub New(errorMessage As String) If (errorMessage Is Nothing) Then Throw New ArgumentNullException("errorMessage") End If Me.ErrorMessage = errorMessage End Sub Public Property ErrorMessage As String Get Return _errorMessage End Get Private Set(value As String) _errorMessage = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean Dim other As InvalidSample = TryCast(obj, InvalidSample) Return Not other Is Nothing AndAlso ErrorMessage = other.ErrorMessage End Function Public Overrides Function GetHashCode() As Integer Return ErrorMessage.GetHashCode() End Function Public Overrides Function ToString() As String Return ErrorMessage End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/ObjectGenerator.vb ================================================ Imports System Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics.CodeAnalysis Imports System.Globalization Imports System.Linq Imports System.Reflection Imports Microsoft.VisualBasic Namespace Areas.HelpPage ''' ''' This class will create an object of a given type and populate it with sample data. ''' Public Class ObjectGenerator Friend Const DefaultCollectionSize As Integer = 2 Private ReadOnly SimpleObjectGenerator As New SimpleTypeObjectGenerator() ''' ''' Generates an object for a given type. The type needs to be public, have a public default constructor and settable public properties/fields. Currently it supports the following types: ''' Simple types: , , , , , etc. ''' Complex types: POCO types. ''' Nullables: . ''' Arrays: arrays of simple types or complex types. ''' Key value pairs: ''' Tuples: , , etc ''' Dictionaries: or anything deriving from . ''' Collections: , , , , , or anything deriving from or . ''' Queryables: , . ''' ''' The type. ''' An object of the given type. Public Function GenerateObject(type As Type) As Object GenerateObject = GenerateObject(type, New Dictionary(Of Type, Object)()) End Function Private Function GenerateObject(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object Try If (SimpleTypeObjectGenerator.CanGenerateObject(type)) Then Return SimpleObjectGenerator.GenerateObject(type) End If If (type.IsArray) Then Return GenerateArray(type, DefaultCollectionSize, createdObjectReferences) End If If (type.IsGenericType) Then Return GenerateGenericType(type, DefaultCollectionSize, createdObjectReferences) End If If (type Is GetType(IDictionary)) Then Return GenerateDictionary(GetType(Hashtable), DefaultCollectionSize, createdObjectReferences) End If If (GetType(IDictionary).IsAssignableFrom(type)) Then Return GenerateDictionary(type, DefaultCollectionSize, createdObjectReferences) End If If (type Is GetType(IList) Or type Is GetType(IEnumerable) Or type Is GetType(ICollection)) Then Return GenerateCollection(GetType(ArrayList), DefaultCollectionSize, createdObjectReferences) End If If (GetType(IList).IsAssignableFrom(type)) Then Return GenerateCollection(type, DefaultCollectionSize, createdObjectReferences) End If If (type Is GetType(IQueryable)) Then Return GenerateQueryable(type, DefaultCollectionSize, createdObjectReferences) End If If (type.IsEnum) Then Return GenerateEnum(type) End If If (type.IsPublic Or type.IsNestedPublic) Then Return GenerateComplexObject(type, createdObjectReferences) End If Catch ' Returns Nothing if anything fails Return Nothing End Try Return Nothing End Function Private Shared Function GenerateGenericType(type As Type, collectionSize As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim genericTypeDefinition As Type = type.GetGenericTypeDefinition() If (genericTypeDefinition Is GetType(Nullable()) Or genericTypeDefinition Is GetType(Nullable(Of ))) Then Return GenerateNullable(type, createdObjectReferences) End If If (genericTypeDefinition Is GetType(KeyValuePair(Of ,))) Then Return GenerateKeyValuePair(type, createdObjectReferences) End If If (IsTuple(genericTypeDefinition)) Then Return GenerateTuple(type, createdObjectReferences) End If Dim genericArguments() As Type = type.GetGenericArguments() If (genericArguments.Length = 1) Then If (genericTypeDefinition Is GetType(IList(Of )) Or genericTypeDefinition Is GetType(IEnumerable(Of )) Or genericTypeDefinition Is GetType(ICollection(Of ))) Then Dim collectionType As Type = GetType(List(Of )).MakeGenericType(genericArguments) Return GenerateCollection(collectionType, collectionSize, createdObjectReferences) End If If (genericTypeDefinition Is GetType(IQueryable(Of ))) Then Return GenerateQueryable(type, collectionSize, createdObjectReferences) End If Dim closedCollectionType As Type = GetType(ICollection(Of )).MakeGenericType(genericArguments(0)) If (closedCollectionType.IsAssignableFrom(type)) Then Return GenerateCollection(type, collectionSize, createdObjectReferences) End If End If If (genericArguments.Length = 2) Then If (genericTypeDefinition Is GetType(IDictionary(Of ,))) Then Dim dictionaryType As Type = GetType(Dictionary(Of ,)).MakeGenericType(genericArguments) Return GenerateDictionary(dictionaryType, collectionSize, createdObjectReferences) End If Dim closedDictionaryType As Type = GetType(IDictionary(Of ,)).MakeGenericType(genericArguments(0), genericArguments(1)) If (closedDictionaryType.IsAssignableFrom(type)) Then Return GenerateDictionary(type, collectionSize, createdObjectReferences) End If End If If (type.IsPublic Or type.IsNestedPublic) Then Return GenerateComplexObject(type, createdObjectReferences) End If Return Nothing End Function Private Shared Function GenerateTuple(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim genericArgs() As Type = type.GetGenericArguments() Dim parameterValues(genericArgs.Length - 1) As Object Dim failedToCreateTuple As Boolean = True Dim objectGenerator As New ObjectGenerator() For i As Integer = 0 To genericArgs.Length - 1 parameterValues(i) = objectGenerator.GenerateObject(genericArgs(i), createdObjectReferences) failedToCreateTuple = failedToCreateTuple And (parameterValues(i) Is Nothing) Next If (failedToCreateTuple) Then Return Nothing End If Return Activator.CreateInstance(type, parameterValues) End Function Private Shared Function IsTuple(genericTypeDefinition As Type) As Boolean Return (genericTypeDefinition Is GetType(Tuple(Of )) Or genericTypeDefinition Is GetType(Tuple(Of ,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,,,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,,,,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,,,,,)) Or genericTypeDefinition Is GetType(Tuple(Of ,,,,,,,))) End Function Private Shared Function GenerateKeyValuePair(keyValuePairType As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim genericArgs() As Type = keyValuePairType.GetGenericArguments() Dim typeK As Type = genericArgs(0) Dim typeV As Type = genericArgs(1) Dim objectGenerator As New ObjectGenerator() Dim keyObject As Object = objectGenerator.GenerateObject(typeK, createdObjectReferences) Dim valueObject As Object = objectGenerator.GenerateObject(typeV, createdObjectReferences) If (keyObject Is Nothing And valueObject Is Nothing) Then ' Failed to create key and values Return Nothing End If Return Activator.CreateInstance(keyValuePairType, keyObject, valueObject) End Function Private Shared Function GenerateArray(arrayType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim type As Type = arrayType.GetElementType() Dim result As Array = Array.CreateInstance(type, size) Dim areAllElementsNothing As Boolean = True Dim objectGenerator As New ObjectGenerator() For i As Integer = 0 To size - 1 Dim element As Object = objectGenerator.GenerateObject(type, createdObjectReferences) result.SetValue(element, i) areAllElementsNothing = areAllElementsNothing And (element Is Nothing) Next If (areAllElementsNothing) Then Return Nothing End If Return result End Function Private Shared Function GenerateDictionary(dictionaryType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim typeK As Type = GetType(Object) Dim typeV As Type = GetType(Object) If (dictionaryType.IsGenericType) Then Dim genericArgs() As Type = dictionaryType.GetGenericArguments() typeK = genericArgs(0) typeV = genericArgs(1) End If Dim result As Object = Activator.CreateInstance(dictionaryType) Dim addMethod As MethodInfo = If(dictionaryType.GetMethod("Add"), dictionaryType.GetMethod("TryAdd")) Dim containsMethod As MethodInfo = If(dictionaryType.GetMethod("Contains"), dictionaryType.GetMethod("ContainsKey")) Dim objectGenerator As New ObjectGenerator() For i As Integer = 0 To size - 1 Dim newKey As Object = objectGenerator.GenerateObject(typeK, createdObjectReferences) If (newKey Is Nothing) Then ' Cannot generate a valid key Return Nothing End If Dim containsKey As Boolean = DirectCast(containsMethod.Invoke(result, New Object() {newKey}), Boolean) If Not containsKey Then Dim newValue As Object = objectGenerator.GenerateObject(typeV, createdObjectReferences) addMethod.Invoke(result, New Object() {newKey, newValue}) End If Next Return result End Function Private Shared Function GenerateEnum(enumType As Type) As Object Dim possibleValues As Array = [Enum].GetValues(enumType) If possibleValues.Length > 0 Then Return possibleValues.GetValue(0) End If Return Nothing End Function Private Shared Function GenerateQueryable(queryableType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim isGeneric As Boolean = queryableType.IsGenericType Dim list As Object = Nothing If (isGeneric) Then Dim listType As Type = GetType(List(Of )).MakeGenericType(queryableType.GetGenericArguments()) list = GenerateCollection(listType, size, createdObjectReferences) Else list = GenerateArray(GetType(Object()), size, createdObjectReferences) End If If (list Is Nothing) Then Return Nothing End If If (isGeneric) Then Dim argumentType As Type = GetType(IEnumerable(Of )).MakeGenericType(queryableType.GetGenericArguments()) Dim asQueryableMethod As MethodInfo = GetType(Queryable).GetMethod("AsQueryable", New Type() {argumentType}) Return asQueryableMethod.Invoke(Nothing, New Object() {list}) End If Return Queryable.AsQueryable(DirectCast(list, IEnumerable)) End Function Private Shared Function GenerateCollection(collectionType As Type, size As Integer, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim type As Type = If(collectionType.IsGenericType, collectionType.GetGenericArguments()(0), GetType(Object)) Dim result As Object = Activator.CreateInstance(collectionType) Dim addMethod As MethodInfo = collectionType.GetMethod("Add") Dim areAllElementsNothing As Boolean = True Dim objectGenerator As New ObjectGenerator() For i As Integer = 0 To size - 1 Dim element As Object = objectGenerator.GenerateObject(type, createdObjectReferences) addMethod.Invoke(result, New Object() {element}) areAllElementsNothing = areAllElementsNothing And (element Is Nothing) Next If (areAllElementsNothing) Then Return Nothing End If Return result End Function Private Shared Function GenerateNullable(nullableType As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim type As Type = nullableType.GetGenericArguments()(0) Dim objectGenerator As New ObjectGenerator() Return objectGenerator.GenerateObject(type, createdObjectReferences) End Function Private Shared Function GenerateComplexObject(type As Type, createdObjectReferences As Dictionary(Of Type, Object)) As Object Dim result As Object = Nothing If (createdObjectReferences.TryGetValue(type, result)) Then ' The object has been created already, just return it. This will handle the circular reference case. Return result End If If (type.IsValueType) Then result = Activator.CreateInstance(type) Else Dim defaultCtor As ConstructorInfo = type.GetConstructor(type.EmptyTypes) If (defaultCtor Is Nothing) Then ' Cannot instantiate the type because it doesn't have a default constructor Return Nothing End If result = defaultCtor.Invoke(New Object() {}) End If createdObjectReferences.Add(type, result) SetPublicProperties(type, result, createdObjectReferences) SetPublicFields(type, result, createdObjectReferences) Return result End Function Private Shared Sub SetPublicProperties(type As Type, obj As Object, createdObjectReferences As Dictionary(Of Type, Object)) Dim properties() As PropertyInfo = type.GetProperties(BindingFlags.Public Or BindingFlags.Instance) Dim objectGenerator As New ObjectGenerator() For Each prop As PropertyInfo In properties If (prop.CanWrite) Then Dim propertyValue As Object = objectGenerator.GenerateObject(prop.PropertyType, createdObjectReferences) prop.SetValue(obj, propertyValue, Nothing) End If Next End Sub Private Shared Sub SetPublicFields(type As Type, obj As Object, createdObjectReferences As Dictionary(Of Type, Object)) Dim fields() As FieldInfo = type.GetFields(BindingFlags.Public Or BindingFlags.Instance) Dim objectGenerator As New ObjectGenerator() For Each field As FieldInfo In fields Dim fieldValue As Object = objectGenerator.GenerateObject(field.FieldType, createdObjectReferences) field.SetValue(obj, fieldValue) Next End Sub Private Class SimpleTypeObjectGenerator Private _index As Long = 0 Private Shared ReadOnly DefaultGenerators As Dictionary(Of Type, Func(Of Long, Object)) = InitializeGenerators() Private Shared Function InitializeGenerators() As Dictionary(Of Type, Func(Of Long, Object)) Return New Dictionary(Of Type, Func(Of Long, Object)) From { {GetType(Boolean), Function(index As Long) True}, {GetType(Byte), Function(index As Long) CByte(64)}, {GetType(Char), Function(index As Long) ChrW(65)}, {GetType(DateTime), Function(index As Long) DateTime.Now}, {GetType(DateTimeOffset), Function(index As Long) New DateTimeOffset(DateTime.Now)}, {GetType(DBNull), Function(index As Long) DBNull.Value}, {GetType(Decimal), Function(index As Long) CDec(index)}, {GetType(Double), Function(index As Long) CDbl(index) + 0.1}, {GetType(Guid), Function(index As Long) Guid.NewGuid()}, {GetType(Int16), Function(index As Long) CType(index Mod Int16.MaxValue, Int16)}, {GetType(Int32), Function(index As Long) CType(index Mod Int32.MaxValue, Int32)}, {GetType(Int64), Function(index As Long) CType(index, Int64)}, {GetType(Object), Function(index As Long) New Object}, {GetType(SByte), Function(index As Long) CSByte(64)}, {GetType(Single), Function(index As Long) CSng(index + 0.1)}, {GetType(String), Function(index As Long) String.Format(CultureInfo.CurrentCulture, "sample string {0}", index)}, {GetType(TimeSpan), Function(index As Long) TimeSpan.FromTicks(1234567)}, {GetType(UInt16), Function(index As Long) CType(index Mod UInt16.MaxValue, UInt16)}, {GetType(UInt32), Function(index As Long) CType(index Mod UInt32.MaxValue, UInt32)}, {GetType(UInt64), Function(index As Long) CType(index, UInt64)}, {GetType(Uri), Function(index As Long) New Uri(String.Format(CultureInfo.CurrentCulture, "http://webapihelppage{0}.com", index))} } End Function Public Shared Function CanGenerateObject(type As Type) As Boolean Return DefaultGenerators.ContainsKey(type) End Function Public Function GenerateObject(type As Type) As Object _index += 1 Return DefaultGenerators(type)(_index) End Function End Class End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/SampleDirection.vb ================================================ Namespace Areas.HelpPage ''' ''' Indicates whether the sample is used for request or response ''' Public Enum SampleDirection Request = 0 Response End Enum End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/SampleGeneration/TextSample.vb ================================================ Imports System Namespace Areas.HelpPage ''' ''' This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. ''' Public Class TextSample Private _text As String Public Sub New(text As String) If (text Is Nothing) Then Throw New ArgumentNullException("text") End If Me.Text = text End Sub Public Property Text As String Get Return _text End Get Private Set(value As String) _text = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean Equals = False Dim other As TextSample = TryCast(obj, TextSample) If Not (other Is Nothing) Then Equals = (Text = other.Text) End If End Function Public Overrides Function GetHashCode() As Integer Return Text.GetHashCode() End Function Public Overrides Function ToString() As String Return Text End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/Api.vbhtml ================================================ @Imports System.Web.Http @Imports System.Web.Http.Description @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @ModelType HelpPageApiModel @Code Dim description As ApiDescription = Model.ApiDescription ViewData("Title") = description.HttpMethod.Method + " " + description.RelativePath End Code
@Html.DisplayForModel()
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.vbhtml ================================================ @Imports System.Web.Http @Imports System.Web.Http.Controllers @Imports System.Web.Http.Description @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage @ModelType IGrouping(Of HttpControllerDescriptor, ApiDescription) @Code Dim controllerDocumentation As String = If(Not ViewData("DocumentationProvider") Is Nothing, ViewData("DocumentationProvider").GetDocumentation(Model.Key), Nothing) End Code

@Model.Key.ControllerName

@If Not controllerDocumentation Is Nothing Then @

@controllerDocumentation

End If @For Each api As ApiDescription In Model @ Next
APIDescription
@api.HttpMethod.Method @api.RelativePath @If Not api.Documentation Is Nothing Then @

@api.Documentation

Else @

No documentation available.

End If
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType CollectionModelDescription @If TypeOf Model.ElementDescription Is ComplexTypeModelDescription Then @Html.DisplayFor(Function(m) m.ElementDescription) End If ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType ComplexTypeModelDescription @Html.DisplayFor(Function(m) Model.Properties, "Parameters") ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType DictionaryModelDescription Dictionary of @Html.DisplayFor(Function(m) Model.KeyModelDescription.ModelType, "ModelDescriptionLink", New With { .modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(Function(m) Model.ValueModelDescription.ModelType, "ModelDescriptionLink", New With { .modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType EnumTypeModelDescription

Possible enumeration values:

@For Each value As EnumValueDescription In Model.Values @ Next
NameValueDescription
@value.Name

@value.Value

@value.Documentation

================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.vbhtml ================================================ @Imports System.Web.Http @Imports System.Web.Http.Description @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.Models @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType HelpPageApiModel @Code Dim description As ApiDescription = Model.ApiDescription End Code

@description.HttpMethod.Method @description.RelativePath

@description.Documentation

Request Information

URI Parameters

@Html.DisplayFor(Function(m) m.UriParameters, "Parameters")

Body Parameters

@Model.RequestDocumentation

@If Model.RequestModelDescription IsNot Nothing Then @Html.DisplayFor(Function(m) m.RequestModelDescription.ModelType, "ModelDescriptionLink", New With {.modelDescription = Model.RequestModelDescription}) If Model.RequestBodyParameters IsNot Nothing Then @Html.DisplayFor(Function(m) m.RequestBodyParameters, "Parameters") End If Else @

None.

End If @If Model.SampleRequests.Count > 0 Then @

Request Formats

@Html.DisplayFor(Function(m) m.SampleRequests, "Samples") End If

Response Information

Resource Description

@description.ResponseDescription.Documentation

@If Model.ResourceDescription IsNot Nothing Then @Html.DisplayFor(Function(m) m.ResourceDescription.ModelType, "ModelDescriptionLink", New With {.modelDescription = Model.ResourceDescription}) If Model.ResourceProperties IsNot Nothing Then @Html.DisplayFor(Function(m) m.ResourceProperties, "Parameters") End If Else @

None.

End If @If Model.SampleResponses.Count > 0 Then @

Response Formats

@Html.DisplayFor(Function(m) m.SampleResponses, "Samples") End If
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage @ModelType ImageSample ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage @ModelType InvalidSample @If HttpContext.Current.IsDebuggingEnabled Then @

@Model.ErrorMessage

Else @

Sample not available.

End If ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType KeyValuePairModelDescription Pair of @Html.DisplayFor(Function(m) Model.KeyModelDescription.ModelType, "ModelDescriptionLink", New With { .modelDescription = Model.KeyModelDescription }) [key] and @Html.DisplayFor(Function(m) Model.ValueModelDescription.ModelType, "ModelDescriptionLink", New With { .modelDescription = Model.ValueModelDescription }) [value] ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType Type @Code Dim modelDescription As ModelDescription = ViewBag.modelDescription If TypeOf modelDescription Is ComplexTypeModelDescription Or TypeOf modelDescription Is EnumTypeModelDescription Then If Model Is GetType(Object) Then @:Object Else @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", New With {.modelName = modelDescription.Name}, Nothing) End If ElseIf TypeOf modelDescription Is CollectionModelDescription Then Dim collectionDescription As CollectionModelDescription = DirectCast(modelDescription, CollectionModelDescription) Dim elementDescription As ModelDescription = collectionDescription.ElementDescription @:Collection of @Html.DisplayFor(Function(m) elementDescription.ModelType, "ModelDescriptionLink", New With {.modelDescription = elementDescription}) Else @Html.DisplayFor(Function(m) modelDescription) End If End Code ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.vbhtml ================================================ @Imports System.Collections.ObjectModel @Imports System.Web.Http.Description @Imports System.Threading @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType IList(Of ParameterDescription) @If Model.Count > 0 Then @ @For Each parameter As ParameterDescription In Model Dim modelDescription As ModelDescription = parameter.TypeDescription @ Next
NameDescriptionTypeAdditional information
@parameter.Name

@parameter.Documentation

@Html.DisplayFor(Function(m) modelDescription.ModelType, "ModelDescriptionLink", New With {.modelDescription = modelDescription}) @If parameter.Annotations.Count > 0 Then @For Each annotation As ParameterAnnotation In parameter.Annotations @

@annotation.Documentation

Next else @

None.

End If
Else @

None.

End If ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.vbhtml ================================================ @Imports System.Net.Http.Headers @ModelType Dictionary(Of MediaTypeHeaderValue, Object) @Code 'Group the samples into a single tab if they are the same. Dim samples As Dictionary(Of String, Object) = Model.GroupBy(Function(pair) pair.Value).ToDictionary( Function(pair) String.Join(", ", pair.Select(Function(m) m.Key.ToString()).ToArray()), Function(pair) pair.Key) Dim mediaTypes As Dictionary(Of String, Object).KeyCollection = samples.Keys End Code
@For Each mediaType As String In mediaTypes @

@mediaType

@
Sample: @Code Dim sample As Object = samples(mediaType) If sample Is Nothing Then @

Sample not available.

Else @Html.DisplayFor(Function(s) sample) End If End code
Next
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType SimpleTypeModelDescription @Model.Documentation ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.vbhtml ================================================ @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage @ModelType TextSample
@Model.Text
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/Index.vbhtml ================================================ @Imports System.Web.Http @Imports System.Web.Http.Controllers @Imports System.Web.Http.Description @Imports System.Collections.ObjectModel @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage @ModelType Collection(Of ApiDescription) @Code ViewData("Title") = "ASP.NET Web API Help Page" ' Group APIs by controller Dim apiGroups As ILookup(Of HttpControllerDescriptor, ApiDescription) = Model.ToLookup(Function(api) api.ActionDescriptor.ControllerDescriptor) End Code

@ViewData("Title")

@For Each group As IGrouping(Of HttpControllerDescriptor, ApiDescription) In apiGroups @Html.DisplayFor(Function(m) group, "ApiGroup") Next
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Help/ResourceModel.vbhtml ================================================ @Imports System.Web.Http @Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions @ModelType ModelDescription

@Model.Name

@Model.Documentation

@Html.DisplayFor(Function(m) Model)
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Shared/_Layout.vbhtml ================================================ @ViewData("Title") @RenderSection("scripts", required:=False) @RenderBody() ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/Web.config ================================================
================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/Views/_ViewStart.vbhtml ================================================ @Code 'Change the Layout path below to blend the look and feel of the help page with your existing web pages. Layout = "~/Areas/HelpPage/Views/Shared/_Layout.vbhtml" End Code ================================================ FILE: src/WebApiHelpPage/VB/Areas/HelpPage/XmlDocumentationProvider.vb ================================================ Imports System Imports System.Globalization Imports System.Linq Imports System.Reflection Imports System.Web.Http.Controllers Imports System.Web.Http.Description Imports System.Xml.XPath Imports ROOT_PROJECT_NAMESPACE.Areas.HelpPage.ModelDescriptions Namespace Areas.HelpPage ''' ''' A custom that reads the API documentation from an XML documentation file. ''' Public Class XmlDocumentationProvider Implements IDocumentationProvider Implements IModelDocumentationProvider Private _documentNavigator As XPathNavigator Private Const TypeExpression As String = "/doc/members/member[@name='T:{0}']" Private Const MethodExpression As String = "/doc/members/member[@name='M:{0}']" Private Const PropertyExpression As String = "/doc/members/member[@name='P:{0}']" Private Const FieldExpression As String = "/doc/members/member[@name='F:{0}']" Private Const ParameterExpression As String = "param[@name='{0}']" ''' ''' Initializes a new instance of the class. ''' ''' The physical path to XML document. Public Sub New(documentPath As String) If (documentPath Is Nothing) Then Throw New ArgumentNullException("documentPath") End If Dim xpath As New XPathDocument(documentPath) _documentNavigator = xpath.CreateNavigator() End Sub Public Function GetDocumentation(controllerDescriptor As HttpControllerDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim typeNode As XPathNavigator = GetTypeNode(controllerDescriptor.ControllerType) Return GetTagValue(typeNode, "summary") End Function Public Function GetDocumentation(actionDescriptor As HttpActionDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim methodNode As XPathNavigator = GetMethodNode(actionDescriptor) Return GetTagValue(methodNode, "summary") End Function Public Function GetDocumentation(parameterDescriptor As HttpParameterDescriptor) As String Implements IDocumentationProvider.GetDocumentation Dim reflectedParameterDescriptor As ReflectedHttpParameterDescriptor = TryCast(parameterDescriptor, ReflectedHttpParameterDescriptor) If (Not reflectedParameterDescriptor Is Nothing) Then Dim methodNode As XPathNavigator = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor) If (Not methodNode Is Nothing) Then Dim parameterName As String = reflectedParameterDescriptor.ParameterInfo.Name Dim parameterNode As XPathNavigator = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName)) If (Not parameterNode Is Nothing) Then Return parameterNode.Value.Trim() End If End If End If Return Nothing End Function Public Function GetResponseDocumentation(actionDescriptor As HttpActionDescriptor) As String Implements IDocumentationProvider.GetResponseDocumentation Dim methodNode As XPathNavigator = GetMethodNode(actionDescriptor) Return GetTagValue(methodNode, "returns") End Function Public Function GetDocumentation(member As MemberInfo) As String Implements IModelDocumentationProvider.GetDocumentation Dim memberName As String = [String].Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name) Dim expression As String = If(member.MemberType = MemberTypes.Field, FieldExpression, PropertyExpression) Dim selectExpression As String = [String].Format(CultureInfo.InvariantCulture, expression, memberName) Dim propertyNode As XPathNavigator = _documentNavigator.SelectSingleNode(selectExpression) Return GetTagValue(propertyNode, "summary") End Function Public Function GetDocumentation(type As Type) As String Implements IModelDocumentationProvider.GetDocumentation Dim typeNode As XPathNavigator = GetTypeNode(type) Return GetTagValue(typeNode, "summary") End Function Private Function GetMethodNode(actionDescriptor As HttpActionDescriptor) As XPathNavigator Dim reflectedActionDescriptor As ReflectedHttpActionDescriptor = TryCast(actionDescriptor, ReflectedHttpActionDescriptor) If (Not reflectedActionDescriptor Is Nothing) Then Dim selectExpression As String = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)) Return _documentNavigator.SelectSingleNode(selectExpression) End If Return Nothing End Function Private Shared Function GetMemberName(method As MethodInfo) As String Dim name As String = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", method.DeclaringType.FullName, method.Name) Dim parameters() As ParameterInfo = method.GetParameters() If (parameters.Length <> 0) Then Dim parameterTypeNames() As String = parameters.Select(Function(param) GetTypeName(param.ParameterType)).ToArray() name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames)) End If Return name End Function Private Shared Function GetTagValue(parentNode As XPathNavigator, tagName As String) As String If (Not parentNode Is Nothing) Then Dim node As XPathNavigator = parentNode.SelectSingleNode(tagName) If (Not node Is Nothing) Then Return node.Value.Trim() End If End If Return Nothing End Function Private Function GetTypeNode(type As Type) As XPathNavigator Dim controllerTypeName As String = GetTypeName(type) Dim selectExpression As String = [String].Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName) Return _documentNavigator.SelectSingleNode(selectExpression) End Function Private Shared Function GetTypeName(type As Type) As String Dim name As String = type.FullName If type.IsGenericType Then ' Format the generic type name to something like: Generic{System.Int32,System.String} Dim genericType As Type = type.GetGenericTypeDefinition() Dim genericArguments As Type() = type.GetGenericArguments() Dim genericTypeName As String = genericType.FullName ' Trim the generic parameter counts from the name genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("`"c)) Dim argumentTypeNames As String() = genericArguments.[Select](Function(t) GetTypeName(t)).ToArray() name = [String].Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, [String].Join(",", argumentTypeNames)) End If If type.IsNested Then ' Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. name = name.Replace("+", ".") End If Return name End Function End Class End Namespace ================================================ FILE: src/WebApiHelpPage/VB/GlobalSuppressions.vb ================================================ ' ' ' This file is used by Code Analysis to maintain SuppressMessage ' attributes that are applied to this project. ' Project-level suppressions either have no target or are given ' a specific target and scoped to a namespace, type, member, etc. ' ' To add a suppression to this file, right-click the message in the ' Code Analysis results, point to "Suppress Message", and click ' "In Suppression File". ' You do not need to add suppressions to this file manually. ================================================ FILE: src/WebApiHelpPage/VB/Properties/AssemblyInfo.vb ================================================ ' Copyright (c) .NET Foundation. All rights reserved. ' Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System Imports System.Reflection Imports System.Runtime.InteropServices ================================================ FILE: src/WebApiHelpPage/VB/Settings.StyleCop ================================================ False ================================================ FILE: src/WebApiHelpPage/VB/WebApiHelpPage.VB.nuspec ================================================  Microsoft.AspNet.WebApi.HelpPage.VB 5.1.0-alpha-0 Microsoft ASP.NET Web API Help Page (VB) Microsoft Microsoft http://www.asp.net/web-api The ASP.NET Web API Help Page automatically generates help page content for the web APIs on your site. Visitors to your help page can use this content to learn how to call your web APIs. Everything generated by the help page is fully customizable using ASP.NET MVC and Razor. ASP.NET Web API Help Page is a great addition to any ASP.NET Web API project. The ASP.NET Web API Help Page automatically generates help page content for the web APIs on your site. en-US Microsoft AspNet WebApi AspNetWebApi HelpPage VB VisualBasic ================================================ FILE: src/WebApiHelpPage/VB/WebApiHelpPageVB.vbproj ================================================  {22075D5A-E9A1-4E2D-ABA7-8E7CBF2A049E} Library ROOT_PROJECT_NAMESPACE WebApiHelpPageVB $(AssemblyName).xml $(CodeAnalysis) ..\..\Strict.ruleset Windows true true false true false true CodeAnalysis On Binary Off On ..\..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll False False Designer Properties\CommonAssemblyInfo.cs {668e9021-ce84-49d9-98fb-df125a9fcdb0} System.Net.Http.Formatting {9b7e3740-6161-4548-833c-4bbca43b970e} System.Web.Helpers {a0187bc2-8325-4bb2-8697-7f955cf4173e} System.Web.Http.WebHost {ddc1ce0c-486e-4e35-bb3b-eab61f8f9440} System.Web.Http {3d3ffd8a-624d-4e9b-954b-e1c105507975} System.Web.Mvc {8f18041b-9410-4c36-a9c5-067813df5f31} System.Web.Razor {22babb60-8f02-4027-affc-acf069954536} System.Web.WebPages.Deployment {0939b11a-fe4e-4ba1-8ad6-d97741ee314f} System.Web.WebPages.Razor {76efa9c5-8d7e-4fdf-b710-e20f8b6b00d2} System.Web.WebPages ================================================ FILE: src/WebApiHelpPage/VB/packages.config ================================================  ================================================ FILE: src/WebApiHelpPage/WebApiHelpPage.csproj ================================================  {FEDFE6CA-8282-4C5B-A756-E97189690982} Library Properties WebApiHelpPage WebApiHelpPage $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETMVC 1591 ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll False False Designer Properties\CommonAssemblyInfo.cs {668e9021-ce84-49d9-98fb-df125a9fcdb0} System.Net.Http.Formatting {9b7e3740-6161-4548-833c-4bbca43b970e} System.Web.Helpers {a0187bc2-8325-4bb2-8697-7f955cf4173e} System.Web.Http.WebHost {ddc1ce0c-486e-4e35-bb3b-eab61f8f9440} System.Web.Http {3d3ffd8a-624d-4e9b-954b-e1c105507975} System.Web.Mvc {8f18041b-9410-4c36-a9c5-067813df5f31} System.Web.Razor {22babb60-8f02-4027-affc-acf069954536} System.Web.WebPages.Deployment {0939b11a-fe4e-4ba1-8ad6-d97741ee314f} System.Web.WebPages.Razor {76efa9c5-8d7e-4fdf-b710-e20f8b6b00d2} System.Web.WebPages ================================================ FILE: src/WebApiHelpPage/WebApiHelpPage.nuspec ================================================  Microsoft.AspNet.WebApi.HelpPage 5.1.0-alpha-0 Microsoft ASP.NET Web API Help Page Microsoft Microsoft http://www.asp.net/web-api The ASP.NET Web API Help Page automatically generates help page content for the web APIs on your site. Visitors to your help page can use this content to learn how to call your web APIs. Everything generated by the help page is fully customizable using ASP.NET MVC and Razor. ASP.NET Web API Help Page is a great addition to any ASP.NET Web API project. The ASP.NET Web API Help Page automatically generates help page content for the web APIs on your site. en-US Microsoft AspNet WebApi AspNetWebApi HelpPage ================================================ FILE: src/WebApiHelpPage/WebApiHelpPageMsBuildTasks.targets ================================================ ================================================ FILE: src/WebApiHelpPage/packages.config ================================================  ================================================ FILE: src/WebHelpers.ruleset ================================================  ================================================ FILE: src/WebMatrix.Data/ConfigurationManagerWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; namespace WebMatrix.Data { internal class ConfigurationManagerWrapper : IConfigurationManager { private readonly string _dataDirectory = null; private IDictionary _appSettings; private IDictionary _handlers; public ConfigurationManagerWrapper(IDictionary handlers, string dataDirectory = null) { Debug.Assert(handlers != null, "handlers should not be null"); _dataDirectory = dataDirectory ?? Database.DataDirectory; _handlers = handlers; } public IDictionary AppSettings { get { if (_appSettings == null) { _appSettings = (from string key in ConfigurationManager.AppSettings select key).ToDictionary(key => key, key => ConfigurationManager.AppSettings[key]); } return _appSettings; } } private static IConnectionConfiguration GetConnectionConfigurationFromConfig(string name) { ConnectionStringSettings setting = ConfigurationManager.ConnectionStrings[name]; if (setting != null) { return new ConnectionConfiguration(setting.ProviderName, setting.ConnectionString); } return null; } public IConnectionConfiguration GetConnection(string name) { return GetConnection(name, GetConnectionConfigurationFromConfig, File.Exists); } // For unit testing internal IConnectionConfiguration GetConnection(string name, Func getConfigConnection, Func fileExists) { // First try config IConnectionConfiguration configuraitonConfig = getConfigConnection(name); if (configuraitonConfig != null) { return configuraitonConfig; } // Then try files under the |DataDirectory| with the supported extensions // REVIEW: We sort because we want to process mdf before sdf (we only have 2 entries) foreach (var handler in _handlers.OrderBy(h => h.Key)) { string fileName = Path.Combine(_dataDirectory, name + handler.Key); if (fileExists(fileName)) { return handler.Value.GetConnectionConfiguration(fileName); } } return null; } } } ================================================ FILE: src/WebMatrix.Data/ConnectionConfiguration.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics; namespace WebMatrix.Data { internal class ConnectionConfiguration : IConnectionConfiguration { internal ConnectionConfiguration(string providerName, string connectionString) : this(new DbProviderFactoryWrapper(providerName), connectionString) { } internal ConnectionConfiguration(IDbProviderFactory providerFactory, string connectionString) { Debug.Assert(!String.IsNullOrEmpty(connectionString), "connectionString should not be null"); ProviderFactory = providerFactory; ConnectionString = connectionString; } public IDbProviderFactory ProviderFactory { get; private set; } public string ConnectionString { get; private set; } } } ================================================ FILE: src/WebMatrix.Data/ConnectionEventArgs.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Data.Common; namespace WebMatrix.Data { public class ConnectionEventArgs : EventArgs { public ConnectionEventArgs(DbConnection connection) { Connection = connection; } public DbConnection Connection { get; private set; } } } ================================================ FILE: src/WebMatrix.Data/Database.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using Microsoft.Internal.Web.Utils; using WebMatrix.Data.Resources; namespace WebMatrix.Data { public class Database : IDisposable { internal const string SqlCeProviderName = "System.Data.SqlServerCe.4.0"; internal const string SqlServerProviderName = "System.Data.SqlClient"; private const string DefaultDataProviderAppSetting = "systemData:defaultProvider"; internal static string DataDirectory = (string)AppDomain.CurrentDomain.GetData("DataDirectory") ?? Directory.GetCurrentDirectory(); private static readonly IDictionary _databaseFileHandlers = new Dictionary(StringComparer.OrdinalIgnoreCase) { { ".sdf", new SqlCeDbFileHandler() }, { ".mdf", new SqlServerDbFileHandler() } }; private static readonly IConfigurationManager _configurationManager = new ConfigurationManagerWrapper(_databaseFileHandlers); private Func _connectionFactory; private DbConnection _connection; internal Database(Func connectionFactory) { _connectionFactory = connectionFactory; } public static event EventHandler ConnectionOpened { add { _connectionOpened += value; } remove { _connectionOpened -= value; } } private static event EventHandler _connectionOpened; public DbConnection Connection { get { if (_connection == null) { _connection = _connectionFactory(); } return _connection; } } public void Close() { Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (_connection != null) { _connection.Close(); _connection = null; } } } public dynamic QuerySingle(string commandText, params object[] args) { if (String.IsNullOrEmpty(commandText)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "commandText"); } return QueryInternal(commandText, args).FirstOrDefault(); } public IEnumerable Query(string commandText, params object[] parameters) { if (String.IsNullOrEmpty(commandText)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "commandText"); } // Return a readonly collection return QueryInternal(commandText, parameters).ToList().AsReadOnly(); } [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Users are responsible for ensuring the inputs to this method are SQL Injection sanitized")] private IEnumerable QueryInternal(string commandText, params object[] parameters) { EnsureConnectionOpen(); DbCommand command = Connection.CreateCommand(); command.CommandText = commandText; AddParameters(command, parameters); using (command) { IEnumerable columnNames = null; using (DbDataReader reader = command.ExecuteReader()) { foreach (DbDataRecord record in reader) { if (columnNames == null) { columnNames = GetColumnNames(record); } yield return new DynamicRecord(columnNames, record); } } } } private static IEnumerable GetColumnNames(DbDataRecord record) { // Get all of the column names for this query for (int i = 0; i < record.FieldCount; i++) { yield return record.GetName(i); } } [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Users are responsible for ensuring the inputs to this method are SQL Injection sanitized")] public int Execute(string commandText, params object[] args) { if (String.IsNullOrEmpty(commandText)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "commandText"); } EnsureConnectionOpen(); DbCommand command = Connection.CreateCommand(); command.CommandText = commandText; AddParameters(command, args); using (command) { return command.ExecuteNonQuery(); } } [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "This makes a database request")] public dynamic GetLastInsertId() { // This method only support sql ce and sql server for now return QueryValue("SELECT @@Identity"); } [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Users are responsible for ensuring the inputs to this method are SQL Injection sanitized")] public dynamic QueryValue(string commandText, params object[] args) { if (String.IsNullOrEmpty(commandText)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "commandText"); } EnsureConnectionOpen(); DbCommand command = Connection.CreateCommand(); command.CommandText = commandText; AddParameters(command, args); using (command) { return command.ExecuteScalar(); } } private void EnsureConnectionOpen() { // If the connection isn't open then open it if (Connection.State != ConnectionState.Open) { Connection.Open(); // Raise the connection opened event OnConnectionOpened(); } } private void OnConnectionOpened() { if (_connectionOpened != null) { _connectionOpened(this, new ConnectionEventArgs(Connection)); } } private static void AddParameters(DbCommand command, object[] args) { if (args == null) { return; } // Create numbered parameters IEnumerable parameters = args.Select((o, index) => { var parameter = command.CreateParameter(); parameter.ParameterName = index.ToString(CultureInfo.InvariantCulture); parameter.Value = o ?? DBNull.Value; return parameter; }); foreach (var p in parameters) { command.Parameters.Add(p); } } public static Database OpenConnectionString(string connectionString) { return OpenConnectionString(connectionString, providerName: null); } public static Database OpenConnectionString(string connectionString, string providerName) { if (String.IsNullOrEmpty(connectionString)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "connectionString"); } return OpenConnectionStringInternal(providerName, connectionString); } public static Database Open(string name) { if (String.IsNullOrEmpty(name)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "name"); } return OpenNamedConnection(name, _configurationManager); } internal static IConnectionConfiguration GetConnectionConfiguration(string fileName, IDictionary handlers) { string extension = Path.GetExtension(fileName); IDbFileHandler handler; if (handlers.TryGetValue(extension, out handler)) { return handler.GetConnectionConfiguration(fileName); } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataResources.UnableToDetermineDatabase, fileName)); } private static Database OpenConnectionStringInternal(string providerName, string connectionString) { return OpenConnectionStringInternal(new DbProviderFactoryWrapper(providerName), connectionString); } private static Database OpenConnectionInternal(IConnectionConfiguration connectionConfig) { return OpenConnectionStringInternal(connectionConfig.ProviderFactory, connectionConfig.ConnectionString); } internal static Database OpenConnectionStringInternal(IDbProviderFactory providerFactory, string connectionString) { return new Database(() => providerFactory.CreateConnection(connectionString)); } internal static Database OpenNamedConnection(string name, IConfigurationManager configurationManager) { // Opens a connection using the connection string setting with the specified name IConnectionConfiguration configuration = configurationManager.GetConnection(name); if (configuration != null) { // We've found one in the connection string setting in config so use it return OpenConnectionInternal(configuration); } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, DataResources.ConnectionStringNotFound, name)); } internal static string GetDefaultProviderName() { string providerName; // Get the default provider name from config if there is any if (!_configurationManager.AppSettings.TryGetValue(DefaultDataProviderAppSetting, out providerName)) { providerName = SqlCeProviderName; } return providerName; } } } ================================================ FILE: src/WebMatrix.Data/DbProviderFactoryWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Data.Common; namespace WebMatrix.Data { internal class DbProviderFactoryWrapper : IDbProviderFactory { private string _providerName; private DbProviderFactory _providerFactory; public DbProviderFactoryWrapper(string providerName) { _providerName = providerName; } public DbConnection CreateConnection(string connectionString) { if (String.IsNullOrEmpty(_providerName)) { // If the provider name is null or empty then use the default _providerName = Database.GetDefaultProviderName(); } if (_providerFactory == null) { _providerFactory = DbProviderFactories.GetFactory(_providerName); } DbConnection connection = _providerFactory.CreateConnection(); connection.ConnectionString = connectionString; return connection; } } } ================================================ FILE: src/WebMatrix.Data/DynamicRecord.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Dynamic; using System.Globalization; using System.Linq; using WebMatrix.Data.Resources; namespace WebMatrix.Data { public sealed class DynamicRecord : DynamicObject, ICustomTypeDescriptor { internal DynamicRecord(IEnumerable columnNames, IDataRecord record) { Debug.Assert(record != null, "record should not be null"); Debug.Assert(columnNames != null, "columnNames should not be null"); Columns = columnNames.ToList(); Record = record; } public IList Columns { get; private set; } private IDataRecord Record { get; set; } public object this[string name] { get { VerifyColumn(name); return GetValue(Record[name]); } } public object this[int index] { get { return GetValue(Record[index]); } } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = this[binder.Name]; return true; } private static object GetValue(object value) { return DBNull.Value == value ? null : value; } public override IEnumerable GetDynamicMemberNames() { return Columns; } private void VerifyColumn(string name) { // REVIEW: Perf if (!Columns.Contains(name, StringComparer.OrdinalIgnoreCase)) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, DataResources.InvalidColumnName, name)); } } AttributeCollection ICustomTypeDescriptor.GetAttributes() { return AttributeCollection.Empty; } string ICustomTypeDescriptor.GetClassName() { return null; } string ICustomTypeDescriptor.GetComponentName() { return null; } TypeConverter ICustomTypeDescriptor.GetConverter() { return null; } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return null; } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; } object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return null; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { return EventDescriptorCollection.Empty; } EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return EventDescriptorCollection.Empty; } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { return ((ICustomTypeDescriptor)this).GetProperties(); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { // Get the name and type for each column name var properties = from columnName in Columns let columnIndex = Record.GetOrdinal(columnName) let type = Record.GetFieldType(columnIndex) select new DynamicPropertyDescriptor(columnName, type); return new PropertyDescriptorCollection(properties.ToArray(), readOnly: true); } object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; } private class DynamicPropertyDescriptor : PropertyDescriptor { private static readonly Attribute[] _empty = new Attribute[0]; private readonly Type _type; public DynamicPropertyDescriptor(string name, Type type) : base(name, _empty) { _type = type; } public override Type ComponentType { get { return typeof(DynamicRecord); } } public override bool IsReadOnly { get { return true; } } public override Type PropertyType { get { return _type; } } public override bool CanResetValue(object component) { return false; } public override object GetValue(object component) { DynamicRecord record = component as DynamicRecord; // REVIEW: Should we throw if the wrong object was passed in? if (record != null) { return record[Name]; } return null; } public override void ResetValue(object component) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, DataResources.RecordIsReadOnly, Name)); } public override void SetValue(object component, object value) { throw new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, DataResources.RecordIsReadOnly, Name)); } public override bool ShouldSerializeValue(object component) { return false; } } } } ================================================ FILE: src/WebMatrix.Data/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "WebMatrix.Data", Justification = "WebMatrix.Data is a logical grouping of data-related helpers, and thus it belongs in its own namespace")] ================================================ FILE: src/WebMatrix.Data/IConfigurationManager.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace WebMatrix.Data { internal interface IConfigurationManager { IDictionary AppSettings { get; } IConnectionConfiguration GetConnection(string name); } } ================================================ FILE: src/WebMatrix.Data/IConnectionConfiguration.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace WebMatrix.Data { internal interface IConnectionConfiguration { string ConnectionString { get; } IDbProviderFactory ProviderFactory { get; } } } ================================================ FILE: src/WebMatrix.Data/IDbFileHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace WebMatrix.Data { internal interface IDbFileHandler { IConnectionConfiguration GetConnectionConfiguration(string fileName); } } ================================================ FILE: src/WebMatrix.Data/IDbProviderFactory.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Data.Common; namespace WebMatrix.Data { internal interface IDbProviderFactory { DbConnection CreateConnection(string connectionString); } } ================================================ FILE: src/WebMatrix.Data/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("WebMatrix.Data")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("WebMatrix.Data.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("WebMatrix.WebData.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] ================================================ FILE: src/WebMatrix.Data/Resources/DataResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace WebMatrix.Data.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class DataResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal DataResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WebMatrix.Data.Resources.DataResources", typeof(DataResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Connection string "{0}" was not found.. /// internal static string ConnectionStringNotFound { get { return ResourceManager.GetString("ConnectionStringNotFound", resourceCulture); } } /// /// Looks up a localized string similar to Invalid column name "{0}".. /// internal static string InvalidColumnName { get { return ResourceManager.GetString("InvalidColumnName", resourceCulture); } } /// /// Looks up a localized string similar to Unable to modify the value of column "{0}" because the record is read only.. /// internal static string RecordIsReadOnly { get { return ResourceManager.GetString("RecordIsReadOnly", resourceCulture); } } /// /// Looks up a localized string similar to Unable to determine the provider for the database file "{0}".. /// internal static string UnableToDetermineDatabase { get { return ResourceManager.GetString("UnableToDetermineDatabase", resourceCulture); } } } } ================================================ FILE: src/WebMatrix.Data/Resources/DataResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Connection string "{0}" was not found. Invalid column name "{0}". Unable to modify the value of column "{0}" because the record is read only. Unable to determine the provider for the database file "{0}". ================================================ FILE: src/WebMatrix.Data/SqlCeDbFileHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics; using System.Globalization; using System.IO; namespace WebMatrix.Data { internal class SqlCeDbFileHandler : IDbFileHandler { private const string SqlCeConnectionStringFormat = @"Data Source={0};File Access Retry Timeout=10"; public IConnectionConfiguration GetConnectionConfiguration(string fileName) { // Get the default provider name string providerName = Database.GetDefaultProviderName(); Debug.Assert(!String.IsNullOrEmpty(providerName), "Provider name should not be null or empty"); string connectionString = GetConnectionString(fileName); return new ConnectionConfiguration(providerName, connectionString); } public static string GetConnectionString(string fileName) { if (Path.IsPathRooted(fileName)) { return String.Format(CultureInfo.InvariantCulture, SqlCeConnectionStringFormat, fileName); } // Use |DataDirectory| if the path isn't rooted string dataSource = @"|DataDirectory|\" + Path.GetFileName(fileName); return String.Format(CultureInfo.InvariantCulture, SqlCeConnectionStringFormat, dataSource); } } } ================================================ FILE: src/WebMatrix.Data/SqlServerDbFileHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.IO; namespace WebMatrix.Data { internal class SqlServerDbFileHandler : IDbFileHandler { private const string SqlServerConnectionStringFormat = @"Data Source=.\SQLEXPRESS;AttachDbFilename={0};Initial Catalog={1};Integrated Security=True;User Instance=True;MultipleActiveResultSets=True"; private const string SqlServerProviderName = "System.Data.SqlClient"; public IConnectionConfiguration GetConnectionConfiguration(string fileName) { return new ConnectionConfiguration(SqlServerProviderName, GetConnectionString(fileName, Database.DataDirectory)); } internal static string GetConnectionString(string fileName, string dataDirectory) { if (Path.IsPathRooted(fileName)) { // Attach the db as the file name if it is rooted return String.Format(CultureInfo.InvariantCulture, SqlServerConnectionStringFormat, fileName, fileName); } // Use |DataDirectory| if the path isn't rooted string dataSource = @"|DataDirectory|\" + Path.GetFileName(fileName); // Set the full path for the initial catalog so we attach as that string initialCatalog = Path.Combine(dataDirectory, Path.GetFileName(fileName)); return String.Format(CultureInfo.InvariantCulture, SqlServerConnectionStringFormat, dataSource, initialCatalog); } } } ================================================ FILE: src/WebMatrix.Data/WebMatrix.Data.csproj ================================================  {4D39BAAF-8A96-473E-AB79-C8A341885137} Library WebMatrix.Data WebMatrix.Data $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True DataResources.resx Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator DataResources.Designer.cs ================================================ FILE: src/WebMatrix.WebData/ConfigUtil.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Configuration; namespace WebMatrix.WebData { internal static class ConfigUtil { private static bool _simpleMembershipEnabled = IsSimpleMembershipEnabled(); public static bool SimpleMembershipEnabled { get { return _simpleMembershipEnabled; } } private static bool IsSimpleMembershipEnabled() { string settingValue = ConfigurationManager.AppSettings[WebSecurity.EnableSimpleMembershipKey]; bool enabled; if (!String.IsNullOrEmpty(settingValue) && Boolean.TryParse(settingValue, out enabled)) { return enabled; } // Simple Membership is enabled by default, but attempts to delegate to the current provider if not initialized. return true; } internal static bool ShouldPreserveLoginUrl() { string settingValue = ConfigurationManager.AppSettings[FormsAuthenticationSettings.PreserveLoginUrlKey]; bool preserveLoginUrl; if (!String.IsNullOrEmpty(settingValue) && Boolean.TryParse(settingValue, out preserveLoginUrl)) { return preserveLoginUrl; } // For backwards compatible with WebPages 1.0, we override the loginUrl value if // the PreserveLoginUrl key is not present. return false; } } } ================================================ FILE: src/WebMatrix.WebData/DatabaseConnectionInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using WebMatrix.Data; namespace WebMatrix.WebData { internal class DatabaseConnectionInfo { private string _connectionStringName; private string _connectionString; private enum ConnectionType { ConnectionStringName = 0, ConnectionString = 1 } public string ConnectionString { get { return _connectionString; } set { _connectionString = value; Type = ConnectionType.ConnectionString; } } public string ConnectionStringName { get { return _connectionStringName; } set { _connectionStringName = value; Type = ConnectionType.ConnectionStringName; } } public string ProviderName { get; set; } private ConnectionType Type { get; set; } public Database Connect() { switch (Type) { case ConnectionType.ConnectionString: return Database.OpenConnectionString(ConnectionString, ProviderName); case ConnectionType.ConnectionStringName: return Database.Open(ConnectionStringName); default: return null; } } } } ================================================ FILE: src/WebMatrix.WebData/DatabaseWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using WebMatrix.Data; namespace WebMatrix.WebData { internal class DatabaseWrapper : IDatabase { private readonly Database _database; public DatabaseWrapper(Database database) { _database = database; } public dynamic QuerySingle(string commandText, params object[] parameters) { return _database.QuerySingle(commandText, parameters); } public IEnumerable Query(string commandText, params object[] parameters) { return _database.Query(commandText, parameters); } public dynamic QueryValue(string commandText, params object[] parameters) { return _database.QueryValue(commandText, parameters); } public int Execute(string commandText, params object[] parameters) { return _database.Execute(commandText, parameters); } public void Dispose() { _database.Dispose(); } } } ================================================ FILE: src/WebMatrix.WebData/ExtendedMembershipProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Security; namespace WebMatrix.WebData { public abstract class ExtendedMembershipProvider : MembershipProvider { private const int OneDayInMinutes = 24 * 60; /// /// Deletes the OAuth and OpenID account with the specified provider name and provider user id. /// /// The provider. /// The provider user id. public virtual void DeleteOAuthAccount(string provider, string providerUserId) { throw new NotImplementedException(); } /// /// Creates a new OAuth account with the specified data or update an existing one if it already exists. /// /// The provider. /// The provider userid. /// The username. public virtual void CreateOrUpdateOAuthAccount(string provider, string providerUserId, string userName) { throw new NotImplementedException(); } /// /// Gets the id of the user with the specified provider name and provider user id. /// /// The provider. /// The provider user id. /// public virtual int GetUserIdFromOAuth(string provider, string providerUserId) { throw new NotImplementedException(); } /// /// Gets the username of a user with the given id /// /// The user id. /// public virtual string GetUserNameFromId(int userId) { throw new NotImplementedException(); } /// /// Determines whether there exists a local account (as opposed to OAuth account) with the specified userId. /// /// The user id to check for local account. /// /// true if there is a local account with the specified user id]; otherwise, false. /// public virtual bool HasLocalAccount(int userId) { throw new NotImplementedException(); } /// /// Gets the OAuth token secret from the specified OAuth token. /// /// The token from which to retrieve secret. /// The token secret of the specified token public virtual string GetOAuthTokenSecret(string token) { throw new NotImplementedException(); } /// /// Stores the specified token and secret into database. /// /// The token. /// The secret. public virtual void StoreOAuthRequestToken(string requestToken, string requestTokenSecret) { throw new NotImplementedException(); } /// /// Replaces the request token with access token and secret in the database. /// /// The request token. /// The access token. /// The access token secret. public virtual void ReplaceOAuthRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) { throw new NotImplementedException(); } /// /// Deletes the OAuth token from the backing store from the database. /// /// The token to be deleted. public virtual void DeleteOAuthToken(string token) { throw new NotImplementedException(); } /// /// Gets all OAuth accounts associated with the specified username /// /// Name of the user. /// public abstract ICollection GetAccountsForUser(string userName); public virtual string CreateUserAndAccount(string userName, string password) { return CreateUserAndAccount(userName, password, requireConfirmation: false, values: null); } public virtual string CreateUserAndAccount(string userName, string password, bool requireConfirmation) { return CreateUserAndAccount(userName, password, requireConfirmation, values: null); } public virtual string CreateUserAndAccount(string userName, string password, IDictionary values) { return CreateUserAndAccount(userName, password, requireConfirmation: false, values: values); } public abstract string CreateUserAndAccount(string userName, string password, bool requireConfirmation, IDictionary values); public virtual string CreateAccount(string userName, string password) { return CreateAccount(userName, password, requireConfirmationToken: false); } public abstract string CreateAccount(string userName, string password, bool requireConfirmationToken); public abstract bool ConfirmAccount(string userName, string accountConfirmationToken); public abstract bool ConfirmAccount(string accountConfirmationToken); public abstract bool DeleteAccount(string userName); public virtual string GeneratePasswordResetToken(string userName) { return GeneratePasswordResetToken(userName, tokenExpirationInMinutesFromNow: OneDayInMinutes); } public abstract string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow); public abstract int GetUserIdFromPasswordResetToken(string token); public abstract bool IsConfirmed(string userName); public abstract bool ResetPasswordWithToken(string token, string newPassword); public abstract int GetPasswordFailuresSinceLastSuccess(string userName); public abstract DateTime GetCreateDate(string userName); public abstract DateTime GetPasswordChangedDate(string userName); public abstract DateTime GetLastPasswordFailureDate(string userName); internal virtual void VerifyInitialized() { } } } ================================================ FILE: src/WebMatrix.WebData/FormsAuthenticationSettings.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; namespace WebMatrix.WebData { /// /// Defines key names for use in a web.config <appSettings> section to override default settings. /// public static class FormsAuthenticationSettings { [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "The term Login is used more frequently in ASP.Net")] public static readonly string LoginUrlKey = "loginUrl"; [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "The term Login is used more frequently in ASP.Net")] public static readonly string DefaultLoginUrl = "~/Account/Login"; [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "The term Login is used more frequently in ASP.Net")] public static readonly string PreserveLoginUrlKey = "PreserveLoginUrl"; } } ================================================ FILE: src/WebMatrix.WebData/GlobalSuppressions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. // // To add a suppression to this file, right-click the message in the // Error List, point to "Suppress Message(s)", and click // "In Project Suppression File". // You do not need to add suppressions to this file manually. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Username", Scope = "member", Target = "WebMatrix.WebData.ExtendedMembershipProvider.#GetUsernameFromId(System.Int32)", Justification = "Username is one word.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "username", Scope = "member", Target = "WebMatrix.WebData.ExtendedMembershipProvider.#GetAccountsForUser(System.String)", Justification = "Username is one word.")] [assembly: SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "username", Scope = "member", Target = "WebMatrix.WebData.ExtendedMembershipProvider.#CreateOrUpdateOAuthAccount(System.String,System.String,System.String)", Justification = "Username is one word.")] ================================================ FILE: src/WebMatrix.WebData/IDatabase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; namespace WebMatrix.WebData { internal interface IDatabase : IDisposable { dynamic QuerySingle(string commandText, params object[] args); IEnumerable Query(string commandText, params object[] parameters); dynamic QueryValue(string commandText, params object[] parameters); int Execute(string commandText, params object[] args); } } ================================================ FILE: src/WebMatrix.WebData/OAuthAccountData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using Microsoft.Internal.Web.Utils; namespace WebMatrix.WebData { /// /// Represents an OpenAuth and OpenID account. /// public class OAuthAccountData { /// /// Initializes a new instance of the class. /// /// The provider. /// The provider user id. public OAuthAccountData(string provider, string providerUserId) { if (String.IsNullOrEmpty(provider)) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "provider"), "provider"); } if (String.IsNullOrEmpty(providerUserId)) { throw new ArgumentException( String.Format(CultureInfo.CurrentCulture, CommonResources.Argument_Cannot_Be_Null_Or_Empty, "providerUserId"), "providerUserId"); } Provider = provider; ProviderUserId = providerUserId; } /// /// Gets the provider name. /// public string Provider { get; private set; } /// /// Gets the provider user id. /// public string ProviderUserId { get; private set; } } } ================================================ FILE: src/WebMatrix.WebData/PreApplicationStartCode.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.ComponentModel; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.WebPages; using System.Web.WebPages.Razor; using WebMatrix.Data; namespace WebMatrix.WebData { [EditorBrowsable(EditorBrowsableState.Never)] public static class PreApplicationStartCode { // NOTE: Do not add public fields, methods, or other members to this class. // This class does not show up in Intellisense so members on it will not be // discoverable by users. Place new members on more appropriate classes that // relate to the public API (for example, a LoginUrl property should go on a // membership-related class). private const string LoginUrlKey = "loginUrl"; private static bool _startWasCalled; public static void Start() { // Even though ASP.NET will only call each PreAppStart once, we sometimes internally call one PreAppStart from // another PreAppStart to ensure that things get initialized in the right order. ASP.NET does not guarantee the // order so we have to guard against multiple calls. // All Start calls are made on same thread, so no lock needed here. if (_startWasCalled) { return; } _startWasCalled = true; // Summary of Simple Membership startup behavior: // 1. If the appSetting enabledSimpleMembership is present and equal to "false", NEITHER SimpleMembership NOR AutoFormsAuth are activated // 2. If the appSetting is true, a non-boolean string or not present, BOTH may be activated // a. SimpleMembership ONLY replaces the AspNetSqlMemberhipProvider, but it does replace it even if it isn't the default. This // means that anything accessing this provider by name will get Simple Membership, but if this provider is no longer the default // then SimpleMembership does not affect the default // b. SimpleMembership delegates to the previous default provider UNLESS WebSecurity.InitializeDatabaseConnection is called. // Initialize membership provider WebSecurity.PreAppStartInit(); // Initialize Forms Authentication default configuration SetUpFormsAuthentication(); // Wire up WebMatrix.Data's Database object to the ASP.NET Web Pages resource tracker Database.ConnectionOpened += OnConnectionOpened; // Auto import the WebMatrix.Data and WebMatrix.WebData namespaces to all apps that are executing. WebPageRazorHost.AddGlobalImport("WebMatrix.Data"); WebPageRazorHost.AddGlobalImport("WebMatrix.WebData"); } private static void OnConnectionOpened(object sender, ConnectionEventArgs e) { // Register all open connections for disposing at the end of the request HttpContext httpContext = HttpContext.Current; if (httpContext != null) { HttpContextWrapper httpContextWrapper = new HttpContextWrapper(httpContext); httpContextWrapper.RegisterForDispose(e.Connection); } } private static void SetUpFormsAuthentication() { if (ConfigUtil.SimpleMembershipEnabled) { NameValueCollection configurationData = new NameValueCollection(); string appSettingsLoginUrl = ConfigurationManager.AppSettings[FormsAuthenticationSettings.LoginUrlKey]; if (appSettingsLoginUrl != null) { // Allow use of as a shortcut to specify // a custom log in url configurationData[LoginUrlKey] = appSettingsLoginUrl; } else if (!ConfigUtil.ShouldPreserveLoginUrl()) { // Otherwise, use the default login url, but only if PreserveLoginUrl != true // If PreserveLoginUrl == true, we do not want to override FormsAuthentication's default // behavior because trying to evaluate FormsAuthentication.LoginUrl at this point in a // PreAppStart method would cause app-relative URLs to be evaluated incorrectly. configurationData[LoginUrlKey] = FormsAuthenticationSettings.DefaultLoginUrl; } FormsAuthentication.EnableFormsAuthentication(configurationData); } } } } ================================================ FILE: src/WebMatrix.WebData/Properties/AssemblyInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Runtime.CompilerServices; using System.Web; using WebMatrix.WebData; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("WebMatrix.WebData")] [assembly: AssemblyDescription("")] [assembly: InternalsVisibleTo("WebPages.Functional.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("WebMatrix.WebData.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] ================================================ FILE: src/WebMatrix.WebData/Resources/WebDataResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.488 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace WebMatrix.WebData.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class WebDataResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal WebDataResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WebMatrix.WebData.Resources.WebDataResources", typeof(WebDataResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Database operation failed.. /// internal static string Security_DbFailure { get { return ResourceManager.GetString("Security_DbFailure", resourceCulture); } } /// /// Looks up a localized string similar to No user table found that has the name "{0}".. /// internal static string Security_FailedToFindUserTable { get { return ResourceManager.GetString("Security_FailedToFindUserTable", resourceCulture); } } /// /// Looks up a localized string similar to The "WebSecurity.InitializeDatabaseConnection" method can be called only once.. /// internal static string Security_InitializeAlreadyCalled { get { return ResourceManager.GetString("Security_InitializeAlreadyCalled", resourceCulture); } } /// /// Looks up a localized string similar to You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class. This call should be placed in an _AppStart.cshtml file in the root of your site.. /// internal static string Security_InitializeMustBeCalledFirst { get { return ResourceManager.GetString("Security_InitializeMustBeCalledFirst", resourceCulture); } } /// /// Looks up a localized string similar to No account exists for "{0}".. /// internal static string Security_NoAccountFound { get { return ResourceManager.GetString("Security_NoAccountFound", resourceCulture); } } /// /// Looks up a localized string similar to To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".. /// internal static string Security_NoExtendedMembershipProvider { get { return ResourceManager.GetString("Security_NoExtendedMembershipProvider", resourceCulture); } } /// /// Looks up a localized string similar to No user found was found that has the name "{0}".. /// internal static string Security_NoUserFound { get { return ResourceManager.GetString("Security_NoUserFound", resourceCulture); } } /// /// Looks up a localized string similar to Failed to store OAuth token to database.. /// internal static string SimpleMembership_FailToStoreOAuthToken { get { return ResourceManager.GetString("SimpleMembership_FailToStoreOAuthToken", resourceCulture); } } /// /// Looks up a localized string similar to The membership password is too long. (Maximum length is 128 characters).. /// internal static string SimpleMembership_PasswordTooLong { get { return ResourceManager.GetString("SimpleMembership_PasswordTooLong", resourceCulture); } } /// /// Looks up a localized string similar to Provider unrecognized attribute: "{0}".. /// internal static string SimpleMembership_ProviderUnrecognizedAttribute { get { return ResourceManager.GetString("SimpleMembership_ProviderUnrecognizedAttribute", resourceCulture); } } /// /// Looks up a localized string similar to The role "{0}" cannot be deleted because there are still users in the role.. /// internal static string SimpleRoleProvder_RolePopulated { get { return ResourceManager.GetString("SimpleRoleProvder_RolePopulated", resourceCulture); } } /// /// Looks up a localized string similar to User "{0}" is already in role "{1}".. /// internal static string SimpleRoleProvder_UserAlreadyInRole { get { return ResourceManager.GetString("SimpleRoleProvder_UserAlreadyInRole", resourceCulture); } } /// /// Looks up a localized string similar to User "{0}" is not in role "{1}".. /// internal static string SimpleRoleProvder_UserNotInRole { get { return ResourceManager.GetString("SimpleRoleProvder_UserNotInRole", resourceCulture); } } /// /// Looks up a localized string similar to No role found that has the name "{0}".. /// internal static string SimpleRoleProvider_NoRoleFound { get { return ResourceManager.GetString("SimpleRoleProvider_NoRoleFound", resourceCulture); } } /// /// Looks up a localized string similar to Role "{0}" already exists.. /// internal static string SimpleRoleProvider_RoleExists { get { return ResourceManager.GetString("SimpleRoleProvider_RoleExists", resourceCulture); } } } } ================================================ FILE: src/WebMatrix.WebData/Resources/WebDataResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Database operation failed. No user table found that has the name "{0}". The "WebSecurity.InitializeDatabaseConnection" method can be called only once. You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class. This call should be placed in an _AppStart.cshtml file in the root of your site. No account exists for "{0}". To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider". No user found was found that has the name "{0}". Failed to store OAuth token to database. The membership password is too long. (Maximum length is 128 characters). Provider unrecognized attribute: "{0}". The role "{0}" cannot be deleted because there are still users in the role. User "{0}" is already in role "{1}". User "{0}" is not in role "{1}". No role found that has the name "{0}". Role "{0}" already exists. ================================================ FILE: src/WebMatrix.WebData/SimpleMembershipProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Configuration.Provider; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Web; using System.Web.Helpers; using System.Web.Security; using System.Web.WebPages; using Microsoft.Internal.Web.Utils; using WebMatrix.Data; using WebMatrix.WebData.Resources; namespace WebMatrix.WebData { public class SimpleMembershipProvider : ExtendedMembershipProvider { private const int TokenSizeInBytes = 16; private readonly MembershipProvider _previousProvider; private SimpleMembershipProviderCasingBehavior _casingBehavior; public SimpleMembershipProvider() : this(null) { } public SimpleMembershipProvider(MembershipProvider previousProvider) { _previousProvider = previousProvider; if (_previousProvider != null) { _previousProvider.ValidatingPassword += (sender, args) => { if (!InitializeCalled) { OnValidatingPassword(args); } }; } } private MembershipProvider PreviousProvider { get { if (_previousProvider == null) { throw new InvalidOperationException(WebDataResources.Security_InitializeMustBeCalledFirst); } else { return _previousProvider; } } } // Public properties // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool EnablePasswordRetrieval { get { return InitializeCalled ? false : PreviousProvider.EnablePasswordRetrieval; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool EnablePasswordReset { get { return InitializeCalled ? false : PreviousProvider.EnablePasswordReset; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool RequiresQuestionAndAnswer { get { return InitializeCalled ? false : PreviousProvider.RequiresQuestionAndAnswer; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool RequiresUniqueEmail { get { return InitializeCalled ? false : PreviousProvider.RequiresUniqueEmail; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipPasswordFormat PasswordFormat { get { return InitializeCalled ? MembershipPasswordFormat.Hashed : PreviousProvider.PasswordFormat; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override int MaxInvalidPasswordAttempts { get { return InitializeCalled ? Int32.MaxValue : PreviousProvider.MaxInvalidPasswordAttempts; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override int PasswordAttemptWindow { get { return InitializeCalled ? Int32.MaxValue : PreviousProvider.PasswordAttemptWindow; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override int MinRequiredPasswordLength { get { return InitializeCalled ? 0 : PreviousProvider.MinRequiredPasswordLength; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override int MinRequiredNonAlphanumericCharacters { get { return InitializeCalled ? 0 : PreviousProvider.MinRequiredNonAlphanumericCharacters; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string PasswordStrengthRegularExpression { get { return InitializeCalled ? String.Empty : PreviousProvider.PasswordStrengthRegularExpression; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string ApplicationName { get { if (InitializeCalled) { throw new NotSupportedException(); } else { return PreviousProvider.ApplicationName; } } set { if (InitializeCalled) { throw new NotSupportedException(); } else { PreviousProvider.ApplicationName = value; } } } internal static string MembershipTableName { get { return "webpages_Membership"; } } internal static string OAuthMembershipTableName { get { return "webpages_OAuthMembership"; } } internal static string OAuthTokenTableName { get { return "webpages_OAuthToken"; } } private string SafeUserTableName { get { return "[" + UserTableName + "]"; } } private string SafeUserIdColumn { get { return "[" + UserIdColumn + "]"; } } private string SafeUserNameColumn { get { return "[" + UserNameColumn + "]"; } } // represents the User table for the app public string UserTableName { get; set; } // represents the User created UserName column, i.e. Email public string UserNameColumn { get; set; } // Represents the User created id column, i.e. ID; // REVIEW: we could get this from the primary key of UserTable in the future public string UserIdColumn { get; set; } /// /// Gets or sets the for this provider. /// /// /// This value configures whether or not queries for user names normalize the user name to uppercase. See /// for a full description. /// public SimpleMembershipProviderCasingBehavior CasingBehavior { get { return _casingBehavior; } set { if (value < SimpleMembershipProviderCasingBehavior.NormalizeCasing || value > SimpleMembershipProviderCasingBehavior.RelyOnDatabaseCollation) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SimpleMembershipProviderCasingBehavior)); } _casingBehavior = value; } } internal DatabaseConnectionInfo ConnectionInfo { get; set; } internal bool InitializeCalled { get; set; } internal override void VerifyInitialized() { if (!InitializeCalled) { throw new InvalidOperationException(WebDataResources.Security_InitializeMustBeCalledFirst); } } // Inherited from ProviderBase - The "previous provider" we get has already been initialized by the Config system, // so we shouldn't forward this call public override void Initialize(string name, NameValueCollection config) { if (config == null) { throw new ArgumentNullException("config"); } if (String.IsNullOrEmpty(name)) { name = "SimpleMembershipProvider"; } if (String.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", "Simple Membership Provider"); } base.Initialize(name, config); config.Remove("connectionStringName"); config.Remove("enablePasswordRetrieval"); config.Remove("enablePasswordReset"); config.Remove("requiresQuestionAndAnswer"); config.Remove("applicationName"); config.Remove("requiresUniqueEmail"); config.Remove("maxInvalidPasswordAttempts"); config.Remove("passwordAttemptWindow"); config.Remove("passwordFormat"); config.Remove("name"); config.Remove("description"); config.Remove("minRequiredPasswordLength"); config.Remove("minRequiredNonalphanumericCharacters"); config.Remove("passwordStrengthRegularExpression"); config.Remove("hashAlgorithmType"); if (config.Count > 0) { string attribUnrecognized = config.GetKey(0); if (!String.IsNullOrEmpty(attribUnrecognized)) { throw new ProviderException(String.Format(CultureInfo.CurrentCulture, WebDataResources.SimpleMembership_ProviderUnrecognizedAttribute, attribUnrecognized)); } } } internal static bool CheckTableExists(IDatabase db, string tableName) { var query = db.QuerySingle(@"SELECT * from INFORMATION_SCHEMA.TABLES where TABLE_NAME = @0", tableName); return query != null; } internal void CreateTablesIfNeeded() { using (var db = ConnectToDatabase()) { if (!CheckTableExists(db, UserTableName)) { db.Execute(@"CREATE TABLE " + SafeUserTableName + "(" + SafeUserIdColumn + " int NOT NULL PRIMARY KEY IDENTITY, " + SafeUserNameColumn + " nvarchar(56) NOT NULL UNIQUE)"); } if (!CheckTableExists(db, OAuthMembershipTableName)) { db.Execute(@"CREATE TABLE " + OAuthMembershipTableName + " (Provider nvarchar(30) NOT NULL, ProviderUserId nvarchar(100) NOT NULL, UserId int NOT NULL, PRIMARY KEY (Provider, ProviderUserId))"); } if (!CheckTableExists(db, MembershipTableName)) { db.Execute(@"CREATE TABLE " + MembershipTableName + @" ( UserId int NOT NULL PRIMARY KEY, CreateDate datetime , ConfirmationToken nvarchar(128) , IsConfirmed bit DEFAULT 0, LastPasswordFailureDate datetime , PasswordFailuresSinceLastSuccess int NOT NULL DEFAULT 0, Password nvarchar(128) NOT NULL, PasswordChangedDate datetime , PasswordSalt nvarchar(128) NOT NULL, PasswordVerificationToken nvarchar(128) , PasswordVerificationTokenExpirationDate datetime)"); // TODO: Do we want to add FK constraint to user table too? // CONSTRAINT fk_UserId FOREIGN KEY (UserId) REFERENCES "+UserTableName+"("+UserIdColumn+"))"); } } } private static void CreateOAuthTokenTableIfNeeded(IDatabase db) { if (!CheckTableExists(db, OAuthTokenTableName)) { db.Execute(@"CREATE TABLE " + OAuthTokenTableName + " (Token nvarchar(100) NOT NULL, Secret nvarchar(100) NOT NULL, PRIMARY KEY (Token))"); } } // Not an override ==> Simple Membership MUST be enabled to use this method public int GetUserId(string userName) { VerifyInitialized(); using (var db = ConnectToDatabase()) { return GetUserId(db, userName); } } private int GetUserId(IDatabase db, string userName) { return GetUserId(db, SafeUserTableName, SafeUserNameColumn, SafeUserIdColumn, CasingBehavior, userName); } internal static int GetUserId( IDatabase db, string userTableName, string userNameColumn, string userIdColumn, SimpleMembershipProviderCasingBehavior casingBehavior, string userName) { dynamic result; if (casingBehavior == SimpleMembershipProviderCasingBehavior.NormalizeCasing) { // Casing is normalized in Sql to allow the database to normalize username according to its collation. The common issue // that can occur here is the 'Turkish i problem', where the uppercase of 'i' is not 'I' in Turkish. result = db.QueryValue(@"SELECT " + userIdColumn + " FROM " + userTableName + " WHERE (UPPER(" + userNameColumn + ") = UPPER(@0))", userName); } else if (casingBehavior == SimpleMembershipProviderCasingBehavior.RelyOnDatabaseCollation) { // When this option is supplied we assume the database has been configured with an appropriate casing, and don't normalize // the user name. This is performant but requires appropriate configuration on the database. result = db.QueryValue(@"SELECT " + userIdColumn + " FROM " + userTableName + " WHERE (" + userNameColumn + " = @0)", userName); } else { Debug.Fail("Unexpected enum value"); return -1; } if (result != null) { return (int)result; } return -1; } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override int GetUserIdFromPasswordResetToken(string token) { VerifyInitialized(); using (var db = ConnectToDatabase()) { var result = db.QuerySingle(@"SELECT UserId FROM " + MembershipTableName + " WHERE (PasswordVerificationToken = @0)", token); if (result != null && result[0] != null) { return (int)result[0]; } return -1; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) { if (!InitializeCalled) { return PreviousProvider.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); } throw new NotSupportedException(); } /// /// Sets the confirmed flag for the username if it is correct. /// /// True if the account could be successfully confirmed. False if the username was not found or the confirmation token is invalid. /// Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override bool ConfirmAccount(string userName, string accountConfirmationToken) { VerifyInitialized(); using (var db = ConnectToDatabase()) { // We need to compare the token using a case insensitive comparison however it seems tricky to do this uniformly across databases when representing the token as a string. // Therefore verify the case on the client var row = db.QuerySingle("SELECT m.[UserId], m.[ConfirmationToken] FROM " + MembershipTableName + " m JOIN " + SafeUserTableName + " u" + " ON m.[UserId] = u." + SafeUserIdColumn + " WHERE m.[ConfirmationToken] = @0 AND" + " u." + SafeUserNameColumn + " = @1", accountConfirmationToken, userName); if (row == null) { return false; } int userId = row[0]; string expectedToken = row[1]; if (String.Equals(accountConfirmationToken, expectedToken, StringComparison.Ordinal)) { int affectedRows = db.Execute("UPDATE " + MembershipTableName + " SET [IsConfirmed] = 1 WHERE [UserId] = @0", userId); return affectedRows > 0; } return false; } } /// /// Sets the confirmed flag for the username if it is correct. /// /// True if the account could be successfully confirmed. False if the username was not found or the confirmation token is invalid. /// Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method. /// There is a tiny possibility where this method fails to work correctly. Two or more users could be assigned the same token but specified using different cases. /// A workaround for this would be to use the overload that accepts both the user name and confirmation token. /// public override bool ConfirmAccount(string accountConfirmationToken) { VerifyInitialized(); using (var db = ConnectToDatabase()) { // We need to compare the token using a case insensitive comparison however it seems tricky to do this uniformly across databases when representing the token as a string. // Therefore verify the case on the client var rows = db.Query("SELECT [UserId], [ConfirmationToken] FROM " + MembershipTableName + " WHERE [ConfirmationToken] = @0", accountConfirmationToken) .Where(r => ((string)r[1]).Equals(accountConfirmationToken, StringComparison.Ordinal)) .ToList(); Debug.Assert(rows.Count < 2, "By virtue of the fact that the ConfirmationToken is random and unique, we can never have two tokens that are identical."); if (!rows.Any()) { return false; } var row = rows.First(); int userId = row[0]; int affectedRows = db.Execute("UPDATE " + MembershipTableName + " SET [IsConfirmed] = 1 WHERE [UserId] = @0", userId); return affectedRows > 0; } } internal virtual IDatabase ConnectToDatabase() { return new DatabaseWrapper(ConnectionInfo.Connect()); } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override string CreateAccount(string userName, string password, bool requireConfirmationToken) { VerifyInitialized(); if (password.IsEmpty()) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } string hashedPassword = Crypto.HashPassword(password); if (hashedPassword.Length > 128) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidPassword); } if (userName.IsEmpty()) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); } using (var db = ConnectToDatabase()) { // Step 1: Check if the user exists in the Users table int uid = GetUserId(db, SafeUserTableName, SafeUserNameColumn, SafeUserIdColumn, CasingBehavior, userName); if (uid == -1) { // User not found throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } // Step 2: Check if the user exists in the Membership table: Error if yes. var result = db.QuerySingle(@"SELECT COUNT(*) FROM [" + MembershipTableName + "] WHERE UserId = @0", uid); if (result[0] > 0) { throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); } // Step 3: Create user in Membership table string token = null; object dbtoken = DBNull.Value; if (requireConfirmationToken) { token = GenerateToken(); dbtoken = token; } int defaultNumPasswordFailures = 0; int insert = db.Execute(@"INSERT INTO [" + MembershipTableName + "] (UserId, [Password], PasswordSalt, IsConfirmed, ConfirmationToken, CreateDate, PasswordChangedDate, PasswordFailuresSinceLastSuccess)" + " VALUES (@0, @1, @2, @3, @4, @5, @5, @6)", uid, hashedPassword, String.Empty /* salt column is unused */, !requireConfirmationToken, dbtoken, DateTime.UtcNow, defaultNumPasswordFailures); if (insert != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } return token; } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status) { if (!InitializeCalled) { return PreviousProvider.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status); } throw new NotSupportedException(); } private void CreateUserRow(IDatabase db, string userName, IDictionary values) { // Make sure user doesn't exist int userId = GetUserId(db, userName); if (userId != -1) { throw new MembershipCreateUserException(MembershipCreateStatus.DuplicateUserName); } StringBuilder columnString = new StringBuilder(); columnString.Append(SafeUserNameColumn); StringBuilder argsString = new StringBuilder(); argsString.Append("@0"); List argsArray = new List(); argsArray.Add(userName); if (values != null) { int index = 1; foreach (string key in values.Keys) { // Skip the user name column since we always generate that if (String.Equals(UserNameColumn, key, StringComparison.OrdinalIgnoreCase)) { continue; } columnString.Append(",").Append(key); argsString.Append(",@").Append(index++); object value = values[key]; if (value == null) { value = DBNull.Value; } argsArray.Add(value); } } int rows = db.Execute("INSERT INTO " + SafeUserTableName + " (" + columnString.ToString() + ") VALUES (" + argsString.ToString() + ")", argsArray.ToArray()); if (rows != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override string CreateUserAndAccount(string userName, string password, bool requireConfirmation, IDictionary values) { VerifyInitialized(); using (var db = ConnectToDatabase()) { CreateUserRow(db, userName, values); return CreateAccount(userName, password, requireConfirmation); } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string GetPassword(string username, string answer) { if (!InitializeCalled) { return PreviousProvider.GetPassword(username, answer); } throw new NotSupportedException(); } private static bool SetPassword(IDatabase db, int userId, string newPassword) { string hashedPassword = Crypto.HashPassword(newPassword); if (hashedPassword.Length > 128) { throw new ArgumentException(WebDataResources.SimpleMembership_PasswordTooLong); } // Update new password int result = db.Execute(@"UPDATE " + MembershipTableName + " SET Password=@0, PasswordSalt=@1, PasswordChangedDate=@2 WHERE UserId = @3", hashedPassword, String.Empty /* salt column is unused */, DateTime.UtcNow, userId); return result > 0; } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool ChangePassword(string username, string oldPassword, string newPassword) { if (!InitializeCalled) { return PreviousProvider.ChangePassword(username, oldPassword, newPassword); } // REVIEW: are commas special in the password? if (username.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "username"); } if (oldPassword.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "oldPassword"); } if (newPassword.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "newPassword"); } using (var db = ConnectToDatabase()) { int userId = GetUserId(db, username); if (userId == -1) { return false; // User not found } // First check that the old credentials match if (!CheckPassword(db, userId, oldPassword)) { return false; } return SetPassword(db, userId, newPassword); } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string ResetPassword(string username, string answer) { if (!InitializeCalled) { return PreviousProvider.ResetPassword(username, answer); } throw new NotSupportedException(); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUser GetUser(object providerUserKey, bool userIsOnline) { if (!InitializeCalled) { return PreviousProvider.GetUser(providerUserKey, userIsOnline); } throw new NotSupportedException(); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUser GetUser(string username, bool userIsOnline) { if (!InitializeCalled) { return PreviousProvider.GetUser(username, userIsOnline); } // Due to a bug in v1, GetUser allows passing null / empty values. using (var db = ConnectToDatabase()) { int userId = GetUserId(db, username); if (userId == -1) { return null; // User not found } return new MembershipUser(Membership.Provider.Name, username, userId, null, null, null, true, false, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue); } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string GetUserNameByEmail(string email) { if (!InitializeCalled) { return PreviousProvider.GetUserNameByEmail(email); } throw new NotSupportedException(); } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override bool DeleteAccount(string userName) { VerifyInitialized(); using (var db = ConnectToDatabase()) { int userId = GetUserId(db, userName); if (userId == -1) { return false; // User not found } int deleted = db.Execute(@"DELETE FROM " + MembershipTableName + " WHERE UserId = @0", userId); return (deleted == 1); } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool DeleteUser(string username, bool deleteAllRelatedData) { if (!InitializeCalled) { return PreviousProvider.DeleteUser(username, deleteAllRelatedData); } using (var db = ConnectToDatabase()) { int userId = GetUserId(db, username); if (userId == -1) { return false; // User not found } int deleted = db.Execute(@"DELETE FROM " + SafeUserTableName + " WHERE " + SafeUserIdColumn + " = @0", userId); bool returnValue = (deleted == 1); //if (deleteAllRelatedData) { // REVIEW: do we really want to delete from the user table? //} return returnValue; } } internal bool DeleteUserAndAccountInternal(string userName) { return (DeleteAccount(userName) && DeleteUser(userName, false)); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { if (!InitializeCalled) { return PreviousProvider.GetAllUsers(pageIndex, pageSize, out totalRecords); } throw new NotSupportedException(); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override int GetNumberOfUsersOnline() { if (!InitializeCalled) { return PreviousProvider.GetNumberOfUsersOnline(); } throw new NotSupportedException(); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords) { if (!InitializeCalled) { return PreviousProvider.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords); } throw new NotSupportedException(); } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords) { if (!InitializeCalled) { return PreviousProvider.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords); } throw new NotSupportedException(); } private static int GetPasswordFailuresSinceLastSuccess(IDatabase db, int userId) { var failure = db.QueryValue(@"SELECT PasswordFailuresSinceLastSuccess FROM " + MembershipTableName + " WHERE (UserId = @0)", userId); if (failure != null) { return failure; } return -1; } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override int GetPasswordFailuresSinceLastSuccess(string userName) { using (var db = ConnectToDatabase()) { int userId = GetUserId(db, userName); if (userId == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, userName)); } return GetPasswordFailuresSinceLastSuccess(db, userId); } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override DateTime GetCreateDate(string userName) { using (var db = ConnectToDatabase()) { int userId = GetUserId(db, userName); if (userId == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, userName)); } var createDate = db.QueryValue(@"SELECT CreateDate FROM " + MembershipTableName + " WHERE (UserId = @0)", userId); if (createDate != null) { return createDate; } return DateTime.MinValue; } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override DateTime GetPasswordChangedDate(string userName) { using (var db = ConnectToDatabase()) { int userId = GetUserId(db, userName); if (userId == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, userName)); } var pwdChangeDate = db.QuerySingle(@"SELECT PasswordChangedDate FROM " + MembershipTableName + " WHERE (UserId = @0)", userId); if (pwdChangeDate != null && pwdChangeDate[0] != null) { return (DateTime)pwdChangeDate[0]; } return DateTime.MinValue; } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override DateTime GetLastPasswordFailureDate(string userName) { using (var db = ConnectToDatabase()) { int userId = GetUserId(db, userName); if (userId == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, userName)); } var failureDate = db.QuerySingle(@"SELECT LastPasswordFailureDate FROM " + MembershipTableName + " WHERE (UserId = @0)", userId); if (failureDate != null && failureDate[0] != null) { return (DateTime)failureDate[0]; } return DateTime.MinValue; } } private bool CheckPassword(IDatabase db, int userId, string password) { string hashedPassword = GetHashedPassword(db, userId); bool verificationSucceeded = (hashedPassword != null && Crypto.VerifyHashedPassword(hashedPassword, password)); if (verificationSucceeded) { // Reset password failure count on successful credential check db.Execute(@"UPDATE " + MembershipTableName + " SET PasswordFailuresSinceLastSuccess = 0 WHERE (UserId = @0)", userId); } else { int failures = GetPasswordFailuresSinceLastSuccess(db, userId); if (failures != -1) { db.Execute(@"UPDATE " + MembershipTableName + " SET PasswordFailuresSinceLastSuccess = @1, LastPasswordFailureDate = @2 WHERE (UserId = @0)", userId, failures + 1, DateTime.UtcNow); } } return verificationSucceeded; } private string GetHashedPassword(IDatabase db, int userId) { var pwdQuery = db.Query(@"SELECT m.[Password] " + @"FROM " + MembershipTableName + " m, " + SafeUserTableName + " u " + @"WHERE m.UserId = " + userId + " AND m.UserId = u." + SafeUserIdColumn).ToList(); // REVIEW: Should get exactly one match, should we throw if we get > 1? if (pwdQuery.Count != 1) { return null; } return pwdQuery[0].Password; } // Ensures the user exists in the accounts table private int VerifyUserNameHasConfirmedAccount(IDatabase db, string username, bool throwException) { int userId = GetUserId(db, username); if (userId == -1) { if (throwException) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, username)); } else { return -1; } } int result = db.QueryValue(@"SELECT COUNT(*) FROM " + MembershipTableName + " WHERE (UserId = @0 AND IsConfirmed = 1)", userId); if (result == 0) { if (throwException) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoAccountFound, username)); } else { return -1; } } return userId; } private static string GenerateToken() { using (var prng = new RNGCryptoServiceProvider()) { return GenerateToken(prng); } } internal static string GenerateToken(RandomNumberGenerator generator) { byte[] tokenBytes = new byte[TokenSizeInBytes]; generator.GetBytes(tokenBytes); return HttpServerUtility.UrlTokenEncode(tokenBytes); } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow) { VerifyInitialized(); if (userName.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "userName"); } using (var db = ConnectToDatabase()) { int userId = VerifyUserNameHasConfirmedAccount(db, userName, throwException: true); string token = db.QueryValue(@"SELECT PasswordVerificationToken FROM " + MembershipTableName + " WHERE (UserId = @0 AND PasswordVerificationTokenExpirationDate > @1)", userId, DateTime.UtcNow); if (token == null) { token = GenerateToken(); int rows = db.Execute(@"UPDATE " + MembershipTableName + " SET PasswordVerificationToken = @0, PasswordVerificationTokenExpirationDate = @1 WHERE (UserId = @2)", token, DateTime.UtcNow.AddMinutes(tokenExpirationInMinutesFromNow), userId); if (rows != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } else { // TODO: should we update expiry again? } return token; } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override bool IsConfirmed(string userName) { VerifyInitialized(); if (userName.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "userName"); } using (var db = ConnectToDatabase()) { int userId = VerifyUserNameHasConfirmedAccount(db, userName, throwException: false); return (userId != -1); } } // Inherited from ExtendedMembershipProvider ==> Simple Membership MUST be enabled to use this method public override bool ResetPasswordWithToken(string token, string newPassword) { VerifyInitialized(); if (newPassword.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "newPassword"); } using (var db = ConnectToDatabase()) { int? userId = db.QueryValue(@"SELECT UserId FROM " + MembershipTableName + " WHERE (PasswordVerificationToken = @0 AND PasswordVerificationTokenExpirationDate > @1)", token, DateTime.UtcNow); if (userId != null) { bool success = SetPassword(db, userId.Value, newPassword); if (success) { // Clear the Token on success int rows = db.Execute(@"UPDATE " + MembershipTableName + " SET PasswordVerificationToken = NULL, PasswordVerificationTokenExpirationDate = NULL WHERE (UserId = @0)", userId); if (rows != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } return success; } else { return false; } } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override void UpdateUser(MembershipUser user) { if (!InitializeCalled) { PreviousProvider.UpdateUser(user); } else { throw new NotSupportedException(); } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool UnlockUser(string userName) { if (!InitializeCalled) { return PreviousProvider.UnlockUser(userName); } throw new NotSupportedException(); } internal void ValidateUserTable() { using (var db = ConnectToDatabase()) { // GetUser will fail with an exception if the user table isn't set up properly try { GetUserId(db, "z"); } catch (Exception e) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, WebDataResources.Security_FailedToFindUserTable, UserTableName), e); } } } // Inherited from MembershipProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool ValidateUser(string username, string password) { if (!InitializeCalled) { return PreviousProvider.ValidateUser(username, password); } if (username.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "username"); } if (password.IsEmpty()) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "password"); } using (var db = ConnectToDatabase()) { int userId = VerifyUserNameHasConfirmedAccount(db, username, throwException: false); if (userId == -1) { return false; } else { return CheckPassword(db, userId, password); } } } public override string GetUserNameFromId(int userId) { VerifyInitialized(); using (var db = ConnectToDatabase()) { dynamic username = db.QueryValue("SELECT " + SafeUserNameColumn + " FROM " + SafeUserTableName + " WHERE (" + SafeUserIdColumn + "=@0)", userId); return (string)username; } } public override void CreateOrUpdateOAuthAccount(string provider, string providerUserId, string userName) { VerifyInitialized(); if (userName.IsEmpty()) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } int userId = GetUserId(userName); if (userId == -1) { throw new MembershipCreateUserException(MembershipCreateStatus.InvalidUserName); } var oldUserId = GetUserIdFromOAuth(provider, providerUserId); using (var db = ConnectToDatabase()) { if (oldUserId == -1) { // account doesn't exist. create a new one. int insert = db.Execute(@"INSERT INTO [" + OAuthMembershipTableName + "] (Provider, ProviderUserId, UserId) VALUES (@0, @1, @2)", provider, providerUserId, userId); if (insert != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } } else { // account already exist. update it int insert = db.Execute(@"UPDATE [" + OAuthMembershipTableName + "] SET UserId = @2 WHERE UPPER(Provider)=@0 AND UPPER(ProviderUserId)=@1", provider, providerUserId, userId); if (insert != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } } } } public override void DeleteOAuthAccount(string provider, string providerUserId) { VerifyInitialized(); using (var db = ConnectToDatabase()) { // account doesn't exist. create a new one. int insert = db.Execute(@"DELETE FROM [" + OAuthMembershipTableName + "] WHERE UPPER(Provider)=@0 AND UPPER(ProviderUserId)=@1", provider, providerUserId); if (insert != 1) { throw new MembershipCreateUserException(MembershipCreateStatus.ProviderError); } } } public override int GetUserIdFromOAuth(string provider, string providerUserId) { VerifyInitialized(); using (var db = ConnectToDatabase()) { dynamic id = db.QueryValue(@"SELECT UserId FROM [" + OAuthMembershipTableName + "] WHERE UPPER(Provider)=@0 AND UPPER(ProviderUserId)=@1", provider.ToUpperInvariant(), providerUserId.ToUpperInvariant()); if (id != null) { return (int)id; } return -1; } } public override string GetOAuthTokenSecret(string token) { VerifyInitialized(); using (var db = ConnectToDatabase()) { CreateOAuthTokenTableIfNeeded(db); // Note that token is case-sensitive dynamic secret = db.QueryValue(@"SELECT Secret FROM [" + OAuthTokenTableName + "] WHERE Token=@0", token); return (string)secret; } } public override void StoreOAuthRequestToken(string requestToken, string requestTokenSecret) { VerifyInitialized(); string existingSecret = GetOAuthTokenSecret(requestToken); if (existingSecret != null) { if (existingSecret == requestTokenSecret) { // the record already exists return; } using (var db = ConnectToDatabase()) { CreateOAuthTokenTableIfNeeded(db); // the token exists with old secret, update it to new secret db.Execute(@"UPDATE [" + OAuthTokenTableName + "] SET Secret = @1 WHERE Token = @0", requestToken, requestTokenSecret); } } else { using (var db = ConnectToDatabase()) { CreateOAuthTokenTableIfNeeded(db); // insert new record int insert = db.Execute(@"INSERT INTO [" + OAuthTokenTableName + "] (Token, Secret) VALUES(@0, @1)", requestToken, requestTokenSecret); if (insert != 1) { throw new ProviderException(WebDataResources.SimpleMembership_FailToStoreOAuthToken); } } } } /// /// Replaces the request token with access token and secret. /// /// The request token. /// The access token. /// The access token secret. public override void ReplaceOAuthRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) { VerifyInitialized(); using (var db = ConnectToDatabase()) { CreateOAuthTokenTableIfNeeded(db); // insert new record db.Execute(@"DELETE FROM [" + OAuthTokenTableName + "] WHERE Token = @0", requestToken); // Although there are two different types of tokens, request token and access token, // we treat them the same in database records. StoreOAuthRequestToken(accessToken, accessTokenSecret); } } /// /// Deletes the OAuth token from the backing store from the database. /// /// The token to be deleted. public override void DeleteOAuthToken(string token) { VerifyInitialized(); using (var db = ConnectToDatabase()) { CreateOAuthTokenTableIfNeeded(db); // Note that token is case-sensitive db.Execute(@"DELETE FROM [" + OAuthTokenTableName + "] WHERE Token=@0", token); } } public override ICollection GetAccountsForUser(string userName) { VerifyInitialized(); int userId = GetUserId(userName); if (userId != -1) { using (var db = ConnectToDatabase()) { IEnumerable records = db.Query(@"SELECT Provider, ProviderUserId FROM [" + OAuthMembershipTableName + "] WHERE UserId=@0", userId); if (records != null) { var accounts = new List(); foreach (DynamicRecord row in records) { accounts.Add(new OAuthAccountData((string)row["Provider"], (string)row["ProviderUserId"])); } return accounts; } } } return new OAuthAccountData[0]; } /// /// Determines whether there exists a local account (as opposed to OAuth account) with the specified userId. /// /// The user id to check for local account. /// /// true if there is a local account with the specified user id]; otherwise, false. /// public override bool HasLocalAccount(int userId) { VerifyInitialized(); using (var db = ConnectToDatabase()) { dynamic id = db.QueryValue(@"SELECT UserId FROM [" + MembershipTableName + "] WHERE UserId=@0", userId); return id != null; } } } } ================================================ FILE: src/WebMatrix.WebData/SimpleMembershipProviderCasingBehavior.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace WebMatrix.WebData { /// /// Configures the behavior of SimpleMembershipProvider for the casing of user name queries. /// public enum SimpleMembershipProviderCasingBehavior { /// /// Uses the SQL Upper function to normalize the casing of user names for a case-insensitive comparison. /// This is the default value. /// /// /// This option uses the SQL Upper function to perform case-normalization. This guarantees that the /// the user name is searched case-insensitively, but can have a performance impact when a large number /// of users exist. /// NormalizeCasing, /// /// Relies on the database's configured collation to normalize casing for the comparison of user names. User /// names are provided to the database exactly as entered by the user. /// /// /// This option relies on the configured collection of database table for user names to perform a correct comparison. /// This is guaranteed to be correct for the chosen collation and performant. Only choose this option if the table storing /// user names is configured with the desired collation. /// RelyOnDatabaseCollation, } } ================================================ FILE: src/WebMatrix.WebData/SimpleRoleProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration.Provider; using System.Globalization; using System.Linq; using System.Web.Security; using WebMatrix.WebData.Resources; namespace WebMatrix.WebData { public class SimpleRoleProvider : RoleProvider { private RoleProvider _previousProvider; private SimpleMembershipProviderCasingBehavior _casingBehavior; public SimpleRoleProvider() : this(null) { } public SimpleRoleProvider(RoleProvider previousProvider) { _previousProvider = previousProvider; } private RoleProvider PreviousProvider { get { if (_previousProvider == null) { throw new InvalidOperationException(WebDataResources.Security_InitializeMustBeCalledFirst); } else { return _previousProvider; } } } private string SafeUserTableName { get { return "[" + UserTableName + "]"; } } private string SafeUserNameColumn { get { return "[" + UserNameColumn + "]"; } } private string SafeUserIdColumn { get { return "[" + UserIdColumn + "]"; } } internal static string RoleTableName { get { return "webpages_Roles"; } } internal static string UsersInRoleTableName { get { return "webpages_UsersInRoles"; } } // represents the User table for the app public string UserTableName { get; set; } // represents the User created UserName column, i.e. Email public string UserNameColumn { get; set; } // Represents the User created id column, i.e. ID; // REVIEW: we could get this from the primary key of UserTable in the future public string UserIdColumn { get; set; } /// /// Gets or sets the for this provider. /// /// /// This value configures whether or not queries for user names normalize the user name to uppercase. See /// for a full description. /// public SimpleMembershipProviderCasingBehavior CasingBehavior { get { return _casingBehavior; } set { if (value < SimpleMembershipProviderCasingBehavior.NormalizeCasing || value > SimpleMembershipProviderCasingBehavior.RelyOnDatabaseCollation) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SimpleMembershipProviderCasingBehavior)); } _casingBehavior = value; } } internal DatabaseConnectionInfo ConnectionInfo { get; set; } internal bool InitializeCalled { get; set; } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string ApplicationName { get { if (InitializeCalled) { throw new NotSupportedException(); } else { return PreviousProvider.ApplicationName; } } set { if (InitializeCalled) { throw new NotSupportedException(); } else { PreviousProvider.ApplicationName = value; } } } private void VerifyInitialized() { if (!InitializeCalled) { throw new InvalidOperationException(WebDataResources.Security_InitializeMustBeCalledFirst); } } private IDatabase ConnectToDatabase() { return new DatabaseWrapper(ConnectionInfo.Connect()); } internal void CreateTablesIfNeeded() { using (var db = ConnectToDatabase()) { if (!SimpleMembershipProvider.CheckTableExists(db, RoleTableName)) { db.Execute(@"CREATE TABLE " + RoleTableName + @" ( RoleId int NOT NULL PRIMARY KEY IDENTITY, RoleName nvarchar(256) NOT NULL UNIQUE)"); db.Execute(@"CREATE TABLE " + UsersInRoleTableName + @" ( UserId int NOT NULL, RoleId int NOT NULL, PRIMARY KEY (UserId, RoleId), CONSTRAINT fk_UserId FOREIGN KEY (UserId) REFERENCES " + SafeUserTableName + "(" + SafeUserIdColumn + @"), CONSTRAINT fk_RoleId FOREIGN KEY (RoleId) REFERENCES " + RoleTableName + "(RoleId) )"); } } } private List GetUserIdsFromNames(IDatabase db, string[] usernames) { List userIds = new List(usernames.Length); foreach (string username in usernames) { int id = SimpleMembershipProvider.GetUserId(db, SafeUserTableName, SafeUserNameColumn, SafeUserIdColumn, CasingBehavior, username); if (id == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, username)); } userIds.Add(id); } return userIds; } private static List GetRoleIdsFromNames(IDatabase db, string[] roleNames) { List roleIds = new List(roleNames.Length); foreach (string role in roleNames) { int id = FindRoleId(db, role); if (id == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.SimpleRoleProvider_NoRoleFound, role)); } roleIds.Add(id); } return roleIds; } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override void AddUsersToRoles(string[] usernames, string[] roleNames) { if (!InitializeCalled) { PreviousProvider.AddUsersToRoles(usernames, roleNames); } else { using (var db = ConnectToDatabase()) { int userCount = usernames.Length; int roleCount = roleNames.Length; List userIds = GetUserIdsFromNames(db, usernames); List roleIds = GetRoleIdsFromNames(db, roleNames); // Generate a INSERT INTO for each userid/rowid combination, where userIds are the first params, and roleIds follow for (int uId = 0; uId < userCount; uId++) { for (int rId = 0; rId < roleCount; rId++) { if (IsUserInRole(usernames[uId], roleNames[rId])) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.SimpleRoleProvder_UserAlreadyInRole, usernames[uId], roleNames[rId])); } // REVIEW: is there a way to batch up these inserts? int rows = db.Execute("INSERT INTO " + UsersInRoleTableName + " VALUES (" + userIds[uId] + "," + roleIds[rId] + "); "); if (rows != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } } } } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override void CreateRole(string roleName) { if (!InitializeCalled) { PreviousProvider.CreateRole(roleName); } else { using (var db = ConnectToDatabase()) { int roleId = FindRoleId(db, roleName); if (roleId != -1) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, WebDataResources.SimpleRoleProvider_RoleExists, roleName)); } int rows = db.Execute("INSERT INTO " + RoleTableName + " (RoleName) VALUES (@0)", roleName); if (rows != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { if (!InitializeCalled) { return PreviousProvider.DeleteRole(roleName, throwOnPopulatedRole); } using (var db = ConnectToDatabase()) { int roleId = FindRoleId(db, roleName); if (roleId == -1) { return false; } if (throwOnPopulatedRole) { int usersInRole = db.Query(@"SELECT * FROM " + UsersInRoleTableName + " WHERE (RoleId = @0)", roleId).Count(); if (usersInRole > 0) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, WebDataResources.SimpleRoleProvder_RolePopulated, roleName)); } } else { // Delete any users in this role first db.Execute(@"DELETE FROM " + UsersInRoleTableName + " WHERE (RoleId = @0)", roleId); } int rows = db.Execute(@"DELETE FROM " + RoleTableName + " WHERE (RoleId = @0)", roleId); return (rows == 1); // REVIEW: should this ever be > 1? } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string[] FindUsersInRole(string roleName, string usernameToMatch) { if (!InitializeCalled) { return PreviousProvider.FindUsersInRole(roleName, usernameToMatch); } using (var db = ConnectToDatabase()) { // REVIEW: Is there any way to directly get out a string[]? List userNames = db.Query(@"SELECT u." + SafeUserNameColumn + " FROM " + SafeUserTableName + " u, " + UsersInRoleTableName + " ur, " + RoleTableName + " r Where (r.RoleName = @0 and ur.RoleId = r.RoleId and ur.UserId = u." + SafeUserIdColumn + " and u." + SafeUserNameColumn + " LIKE @1)", new object[] { roleName, usernameToMatch }).ToList(); string[] users = new string[userNames.Count]; for (int i = 0; i < userNames.Count; i++) { users[i] = (string)userNames[i][0]; } return users; } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string[] GetAllRoles() { if (!InitializeCalled) { return PreviousProvider.GetAllRoles(); } using (var db = ConnectToDatabase()) { return db.Query(@"SELECT RoleName FROM " + RoleTableName).Select(d => (string)d[0]).ToArray(); } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string[] GetRolesForUser(string username) { if (!InitializeCalled) { return PreviousProvider.GetRolesForUser(username); } using (var db = ConnectToDatabase()) { int userId = SimpleMembershipProvider.GetUserId(db, SafeUserTableName, SafeUserNameColumn, SafeUserIdColumn, CasingBehavior, username); if (userId == -1) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.Security_NoUserFound, username)); } string query = @"SELECT r.RoleName FROM " + UsersInRoleTableName + " u, " + RoleTableName + " r Where (u.UserId = @0 and u.RoleId = r.RoleId) GROUP BY RoleName"; return db.Query(query, new object[] { userId }).Select(d => (string)d[0]).ToArray(); } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override string[] GetUsersInRole(string roleName) { if (!InitializeCalled) { return PreviousProvider.GetUsersInRole(roleName); } using (var db = ConnectToDatabase()) { string query = @"SELECT u." + SafeUserNameColumn + " FROM " + SafeUserTableName + " u, " + UsersInRoleTableName + " ur, " + RoleTableName + " r Where (r.RoleName = @0 and ur.RoleId = r.RoleId and ur.UserId = u." + SafeUserIdColumn + ")"; return db.Query(query, new object[] { roleName }).Select(d => (string)d[0]).ToArray(); } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool IsUserInRole(string username, string roleName) { if (!InitializeCalled) { return PreviousProvider.IsUserInRole(username, roleName); } using (var db = ConnectToDatabase()) { var count = db.QuerySingle("SELECT COUNT(*) FROM " + SafeUserTableName + " u, " + UsersInRoleTableName + " ur, " + RoleTableName + " r Where (u." + SafeUserNameColumn + " = @0 and r.RoleName = @1 and ur.RoleId = r.RoleId and ur.UserId = u." + SafeUserIdColumn + ")", username, roleName); return (count[0] == 1); } } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { if (!InitializeCalled) { PreviousProvider.RemoveUsersFromRoles(usernames, roleNames); } else { foreach (string rolename in roleNames) { if (!RoleExists(rolename)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.SimpleRoleProvider_NoRoleFound, rolename)); } } foreach (string username in usernames) { foreach (string rolename in roleNames) { if (!IsUserInRole(username, rolename)) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebDataResources.SimpleRoleProvder_UserNotInRole, username, rolename)); } } } using (var db = ConnectToDatabase()) { List userIds = GetUserIdsFromNames(db, usernames); List roleIds = GetRoleIdsFromNames(db, roleNames); foreach (int userId in userIds) { foreach (int roleId in roleIds) { // Review: Is there a way to do these all in one query? int rows = db.Execute("DELETE FROM " + UsersInRoleTableName + " WHERE UserId = " + userId + " and RoleId = " + roleId); if (rows != 1) { throw new ProviderException(WebDataResources.Security_DbFailure); } } } } } } private static int FindRoleId(IDatabase db, string roleName) { var result = db.QuerySingle(@"SELECT RoleId FROM " + RoleTableName + " WHERE (RoleName = @0)", roleName); if (result == null) { return -1; } return (int)result[0]; } // Inherited from RoleProvider ==> Forwarded to previous provider if this provider hasn't been initialized public override bool RoleExists(string roleName) { if (!InitializeCalled) { return PreviousProvider.RoleExists(roleName); } using (var db = ConnectToDatabase()) { return (FindRoleId(db, roleName) != -1); } } } } ================================================ FILE: src/WebMatrix.WebData/WebMatrix.WebData.csproj ================================================  {55A15F40-1435-4248-A7F2-2A146BB83586} Library WebMatrix.WebData WebMatrix.WebData $(OutputPath)$(AssemblyName).xml $(CodeAnalysis) ..\Strict.ruleset $(DefineConstants);ASPNETWEBPAGES 1591 Properties\CommonAssemblyInfo.cs Common\CommonResources.Designer.cs True True CommonResources.resx Common\GlobalSuppressions.cs True True WebDataResources.resx {9B7E3740-6161-4548-833C-4BBCA43B970E} System.Web.Helpers {8F18041B-9410-4C36-A9C5-067813DF5F31} System.Web.Razor {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} System.Web.WebPages.Razor {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {4D39BAAF-8A96-473E-AB79-C8A341885137} WebMatrix.Data Common\CommonResources.resx ResXFileCodeGenerator CommonResources.Designer.cs ResXFileCodeGenerator WebDataResources.Designer.cs ================================================ FILE: src/WebMatrix.WebData/WebSecurity.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Web; using System.Web.Routing; using System.Web.Security; using System.Web.WebPages; using WebMatrix.WebData.Resources; namespace WebMatrix.WebData { public static class WebSecurity { public static readonly string EnableSimpleMembershipKey = "enableSimpleMembership"; /// /// Gets a value indicating whether the method has been initialized. /// /// /// true if initialized; otherwise, false. /// public static bool Initialized { get; private set; } public static int CurrentUserId { get { return GetUserId(CurrentUserName); } } public static string CurrentUserName { get { return Context.User.Identity.Name; } } public static bool HasUserId { get { return CurrentUserId != -1; } } public static bool IsAuthenticated { get { return Request.IsAuthenticated; } } internal static HttpContextBase Context { get { return new HttpContextWrapper(HttpContext.Current); } } internal static HttpRequestBase Request { get { return Context.Request; } } internal static HttpResponseBase Response { get { return Context.Response; } } internal static void PreAppStartInit() { // Allow use of to disable registration of membership/role providers as default. if (ConfigUtil.SimpleMembershipEnabled) { // called during PreAppStart, should also hook up the config for MembershipProviders? // Replace the AspNetSqlMembershipProvider (which is the default that is registered in root web.config) const string BuiltInMembershipProviderName = "AspNetSqlMembershipProvider"; var builtInMembership = Membership.Providers[BuiltInMembershipProviderName]; if (builtInMembership != null) { var simpleMembership = CreateDefaultSimpleMembershipProvider(BuiltInMembershipProviderName, currentDefault: builtInMembership); Membership.Providers.Remove(BuiltInMembershipProviderName); Membership.Providers.Add(simpleMembership); } Roles.Enabled = true; const string BuiltInRolesProviderName = "AspNetSqlRoleProvider"; var builtInRoles = Roles.Providers[BuiltInRolesProviderName]; if (builtInRoles != null) { var simpleRoles = CreateDefaultSimpleRoleProvider(BuiltInRolesProviderName, currentDefault: builtInRoles); Roles.Providers.Remove(BuiltInRolesProviderName); Roles.Providers.Add(simpleRoles); } } } private static ExtendedMembershipProvider VerifyProvider() { ExtendedMembershipProvider provider = Membership.Provider as ExtendedMembershipProvider; if (provider == null) { throw new InvalidOperationException(WebDataResources.Security_NoExtendedMembershipProvider); } provider.VerifyInitialized(); // Have the provider verify that it's initialized (only our SimpleMembershipProvider does anything here) return provider; } public static void InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables) { InitializeDatabaseConnection( connectionStringName, userTableName, userIdColumn, userNameColumn, autoCreateTables, SimpleMembershipProviderCasingBehavior.NormalizeCasing); } public static void InitializeDatabaseConnection( string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables, SimpleMembershipProviderCasingBehavior casingBehavior) { DatabaseConnectionInfo connect = new DatabaseConnectionInfo(); connect.ConnectionStringName = connectionStringName; InitializeProviders(connect, userTableName, userIdColumn, userNameColumn, autoCreateTables, casingBehavior); } public static void InitializeDatabaseConnection( string connectionString, string providerName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables) { InitializeDatabaseConnection( connectionString, providerName, userTableName, userIdColumn, userNameColumn, autoCreateTables, SimpleMembershipProviderCasingBehavior.NormalizeCasing); } public static void InitializeDatabaseConnection( string connectionString, string providerName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables, SimpleMembershipProviderCasingBehavior casingBehavior) { DatabaseConnectionInfo connect = new DatabaseConnectionInfo(); connect.ConnectionString = connectionString; connect.ProviderName = providerName; InitializeProviders(connect, userTableName, userIdColumn, userNameColumn, autoCreateTables, casingBehavior); } private static void InitializeProviders( DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables, SimpleMembershipProviderCasingBehavior casingBehavior) { SimpleMembershipProvider simpleMembership = Membership.Provider as SimpleMembershipProvider; if (simpleMembership != null) { InitializeMembershipProvider(simpleMembership, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables, casingBehavior); } SimpleRoleProvider simpleRoles = Roles.Provider as SimpleRoleProvider; if (simpleRoles != null) { InitializeRoleProvider(simpleRoles, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables, casingBehavior); } Initialized = true; } internal static void InitializeMembershipProvider( SimpleMembershipProvider simpleMembership, DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool createTables, SimpleMembershipProviderCasingBehavior casingBehavior) { if (simpleMembership.InitializeCalled) { throw new InvalidOperationException(WebDataResources.Security_InitializeAlreadyCalled); } simpleMembership.CasingBehavior = casingBehavior; simpleMembership.ConnectionInfo = connect; simpleMembership.UserIdColumn = userIdColumn; simpleMembership.UserNameColumn = userNameColumn; simpleMembership.UserTableName = userTableName; if (createTables) { simpleMembership.CreateTablesIfNeeded(); } else { // We want to validate the user table if we aren't creating them simpleMembership.ValidateUserTable(); } simpleMembership.InitializeCalled = true; } internal static void InitializeRoleProvider( SimpleRoleProvider simpleRoles, DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool createTables, SimpleMembershipProviderCasingBehavior casingBehavior) { if (simpleRoles.InitializeCalled) { throw new InvalidOperationException(WebDataResources.Security_InitializeAlreadyCalled); } simpleRoles.CasingBehavior = casingBehavior; simpleRoles.ConnectionInfo = connect; simpleRoles.UserTableName = userTableName; simpleRoles.UserIdColumn = userIdColumn; simpleRoles.UserNameColumn = userNameColumn; if (createTables) { simpleRoles.CreateTablesIfNeeded(); } simpleRoles.InitializeCalled = true; } private static SimpleMembershipProvider CreateDefaultSimpleMembershipProvider(string name, MembershipProvider currentDefault) { var membership = new SimpleMembershipProvider(previousProvider: currentDefault); NameValueCollection config = new NameValueCollection(); membership.Initialize(name, config); return membership; } private static SimpleRoleProvider CreateDefaultSimpleRoleProvider(string name, RoleProvider currentDefault) { var roleProvider = new SimpleRoleProvider(previousProvider: currentDefault); NameValueCollection config = new NameValueCollection(); roleProvider.Initialize(name, config); return roleProvider; } [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "Login is used more consistently in ASP.Net")] [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")] public static bool Login(string userName, string password, bool persistCookie = false) { VerifyProvider(); bool success = Membership.ValidateUser(userName, password); if (success) { FormsAuthentication.SetAuthCookie(userName, persistCookie); } return success; } [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "Login is used more consistently in ASP.Net")] public static void Logout() { VerifyProvider(); FormsAuthentication.SignOut(); } public static bool ChangePassword(string userName, string currentPassword, string newPassword) { VerifyProvider(); bool success = false; try { var currentUser = Membership.GetUser(userName, true /* userIsOnline */); success = currentUser.ChangePassword(currentPassword, newPassword); } catch (ArgumentException) { // An argument exception is thrown if the new password does not meet the provider's requirements } return success; } public static bool ConfirmAccount(string accountConfirmationToken) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.ConfirmAccount(accountConfirmationToken); } public static bool ConfirmAccount(string userName, string accountConfirmationToken) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.ConfirmAccount(userName, accountConfirmationToken); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")] public static string CreateAccount(string userName, string password, bool requireConfirmationToken = false) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.CreateAccount(userName, password, requireConfirmationToken); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")] public static string CreateUserAndAccount(string userName, string password, object propertyValues = null, bool requireConfirmationToken = false) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this IDictionary values = propertyValues as RouteValueDictionary; if (values == null && propertyValues != null) { var propertyValuesAsDictionary = propertyValues as IDictionary; if (propertyValuesAsDictionary != null) { values = new RouteValueDictionary(propertyValuesAsDictionary); } else { values = new RouteValueDictionary(propertyValues); } } return provider.CreateUserAndAccount(userName, password, requireConfirmationToken, values); } [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")] public static string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow = 1440) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GeneratePasswordResetToken(userName, tokenExpirationInMinutesFromNow); } public static bool UserExists(string userName) { VerifyProvider(); return Membership.GetUser(userName) != null; } public static int GetUserId(string userName) { VerifyProvider(); MembershipUser user = Membership.GetUser(userName); if (user == null) { return -1; } // REVIEW: This cast is breaking the abstraction for the membershipprovider, we basically assume that userids are ints return (int)user.ProviderUserKey; } public static int GetUserIdFromPasswordResetToken(string token) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GetUserIdFromPasswordResetToken(token); } public static bool IsCurrentUser(string userName) { VerifyProvider(); return String.Equals(CurrentUserName, userName, StringComparison.OrdinalIgnoreCase); } public static bool IsConfirmed(string userName) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.IsConfirmed(userName); } // Make sure the logged on user is same as the one specified by the id private static bool IsUserLoggedOn(int userId) { VerifyProvider(); return CurrentUserId == userId; } // Make sure the user was authenticated public static void RequireAuthenticatedUser() { VerifyProvider(); var user = Context.User; if (user == null || !user.Identity.IsAuthenticated) { Response.SetStatus(HttpStatusCode.Unauthorized); } } // Make sure the user was authenticated public static void RequireUser(int userId) { VerifyProvider(); if (!IsUserLoggedOn(userId)) { Response.SetStatus(HttpStatusCode.Unauthorized); } } public static void RequireUser(string userName) { VerifyProvider(); if (!String.Equals(CurrentUserName, userName, StringComparison.OrdinalIgnoreCase)) { Response.SetStatus(HttpStatusCode.Unauthorized); } } public static void RequireRoles(params string[] roles) { VerifyProvider(); foreach (string role in roles) { if (!Roles.IsUserInRole(CurrentUserName, role)) { Response.SetStatus(HttpStatusCode.Unauthorized); return; } } } public static bool ResetPassword(string passwordResetToken, string newPassword) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.ResetPasswordWithToken(passwordResetToken, newPassword); } public static bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, int intervalInSeconds) { VerifyProvider(); return IsAccountLockedOut(userName, allowedPasswordAttempts, TimeSpan.FromSeconds(intervalInSeconds)); } public static bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, TimeSpan interval) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return IsAccountLockedOutInternal(provider, userName, allowedPasswordAttempts, interval); } internal static bool IsAccountLockedOutInternal(ExtendedMembershipProvider provider, string userName, int allowedPasswordAttempts, TimeSpan interval) { return (provider.GetUser(userName, false) != null && provider.GetPasswordFailuresSinceLastSuccess(userName) > allowedPasswordAttempts && provider.GetLastPasswordFailureDate(userName).Add(interval) > DateTime.UtcNow); } public static int GetPasswordFailuresSinceLastSuccess(string userName) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GetPasswordFailuresSinceLastSuccess(userName); } public static DateTime GetCreateDate(string userName) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GetCreateDate(userName); } public static DateTime GetPasswordChangedDate(string userName) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GetPasswordChangedDate(userName); } public static DateTime GetLastPasswordFailureDate(string userName) { ExtendedMembershipProvider provider = VerifyProvider(); Debug.Assert(provider != null); // VerifyProvider checks this return provider.GetLastPasswordFailureDate(userName); } } } ================================================ FILE: test/Common/AttributeListTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using Microsoft.TestCommon; namespace System.ComponentModel { public class AttributeListTest { private readonly Attribute[] _testAttributes; private readonly AttributeCollection _collection; private readonly AttributeList _list; public AttributeListTest() { _testAttributes = new Attribute[] { new TestAttribute(), new DerivedAttribute(), new DerivedDerivedAttribute() }; _collection = new AttributeCollection(_testAttributes); _list = new AttributeList(_collection); } [Fact] public void AttributeListCountMatchesWrapped() { Assert.Equal(_collection.Count, _list.Count); } [Fact] public void AttributeListIsReadOnlyTrue() { Assert.True(_list.IsReadOnly); } [Fact] public void AttributeListIndexerMatchesWrapped() { Assert.Equal(_collection[1], _list[1]); } [Fact] public void AttributeListAddThrows() { Assert.Throws(() => _list.Add(null)); } [Fact] public void AttributeListClearThrows() { Assert.Throws(() => _list.Clear()); } [Fact] public void AttributeListContainsWrappedTrue() { Attribute presentAttribute = _collection[2]; Assert.Contains(presentAttribute, _list); } [Fact] public void AttributeListContainsMissingFalse() { Attribute missingAttribute = new MissingAttribute(); Assert.DoesNotContain(missingAttribute, _list); } [Fact] public void AttributeListCopyToResultsEqual() { Attribute[] arrayCopy = new Attribute[3]; _list.CopyTo(arrayCopy, 0); Assert.Equal(_list, arrayCopy); } [Fact] public void AttributeListIndexOfMatchesIndexer() { Assert.Equal(1, _list.IndexOf(_list[1])); } [Fact] public void AttributeListRemoveAtThrows() { Assert.Throws(() => _list.RemoveAt(0)); Assert.Throws(() => ((ICollection)_list).Remove(_list[0])); } [Fact] public void AttributeListEnumerationMatchesWrapped() { int i = 0; foreach (Attribute attribute in _list) { Assert.Equal(_collection[i], attribute); i++; } Assert.Equal(_collection.Count, i); i = 0; IEnumerable asEumerable = _list as IEnumerable; foreach (Attribute attribute in asEumerable) { Assert.Equal(_collection[i], attribute); i++; } Assert.Equal(_collection.Count, i); } private class TestAttribute : Attribute { public TestAttribute() { } } private class DerivedAttribute: TestAttribute { public DerivedAttribute() { } } private class DerivedDerivedAttribute : DerivedAttribute { public DerivedDerivedAttribute() { } } private class MissingAttribute : Attribute { public MissingAttribute() { } } } } ================================================ FILE: test/Common/CollectionExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Linq; using Microsoft.TestCommon; namespace System.Collections.Generic { public class CollectionExtensionsTest { [Fact] public void AppendAndReallocateEmpty_ReturnsOne() { string[] empty = new string[0]; string[] emptyAppended = empty.AppendAndReallocate("AppendedEmpty"); string singleAppended = Assert.Single(emptyAppended); Assert.Equal("AppendedEmpty", singleAppended); } [Fact] public void AppendAndReallocateOne_ReturnsTwo() { string[] one = new string[] { "One" }; string[] oneAppended = one.AppendAndReallocate("AppendedOne"); Assert.Equal(2, oneAppended.Length); Assert.Equal("One", oneAppended[0]); Assert.Equal("AppendedOne", oneAppended[1]); } [Fact] public void AsArray_Array_ReturnsSameInstance() { object[] array = new object[] { new object(), new object() }; object[] arrayAsArray = ((IEnumerable)array).AsArray(); Assert.Same(array, arrayAsArray); } [Fact] public void AsArray_Enumerable_Copies() { IList list = new List() { new object(), new object() }; object[] listToArray = list.ToArray(); object[] listAsArray = ((IEnumerable)list).AsArray(); Assert.Equal(listToArray, listAsArray); } [Fact] public void AsCollection_Collection_ReturnsSameInstance() { Collection collection = new Collection() { new object(), new object() }; Collection collectionAsCollection = ((IEnumerable)collection).AsCollection(); Assert.Same(collection, collectionAsCollection); } [Fact] public void AsCollection_Enumerable_Copies() { IEnumerable enumerable = new LinkedList(new object[] { new object(), new object() }); Collection enumerableAsCollection = ((IEnumerable)enumerable).AsCollection(); Assert.Equal(enumerable, ((IEnumerable)enumerableAsCollection)); } [Fact] public void AsCollection_IList_Wraps() { IList list = new List() { new object(), new object() }; Collection listAsCollection = list.AsCollection(); list.Add(new object()); Assert.Equal(list, listAsCollection.ToList()); } [Fact] public void AsIList_IList_ReturnsSameInstance() { List list = new List { new object(), new object() }; IList listAsIList = ((IEnumerable)list).AsIList(); Assert.Same(list, listAsIList); } [Fact] public void AsIList_Enumerable_Copies() { LinkedList enumerable = new LinkedList(); enumerable.AddLast(new object()); enumerable.AddLast(new object()); List expected = enumerable.ToList(); IList enumerableAsIList = ((IEnumerable)enumerable).AsIList(); Assert.Equal(expected, enumerableAsIList); Assert.NotSame(expected, enumerableAsIList); } [Fact] public void AsList_List_ReturnsSameInstance() { List list = new List { new object(), new object() }; List listAsList = ((IEnumerable)list).AsList(); Assert.Same(list, listAsList); } [Fact] public void AsList_Enumerable_Copies() { List list = new List() { new object(), new object() }; object[] array = list.ToArray(); List arrayAsList = ((IEnumerable)array).AsList(); Assert.Equal(list, arrayAsList); Assert.NotSame(list, arrayAsList); Assert.NotSame(array, arrayAsList); } [Fact] public void AsList_ListWrapperCollection_ReturnsSameInstance() { List list = new List { new object(), new object() }; ListWrapperCollection listWrapper = new ListWrapperCollection(list); List listWrapperAsList = ((IEnumerable)listWrapper).AsList(); Assert.Same(list, listWrapperAsList); } [Fact] void RemoveFromTwoElementsAtEnd_NoChange() { List list = new List() { new object(), new object() }; List listExpected = new List(list); list.RemoveFrom(2); Assert.Equal(listExpected, list); } [Fact] void RemoveFromTwoElementsMiddle_ToOne() { List list = new List() { new object(), new object() }; List listExpected = new List() { list[0] }; list.RemoveFrom(1); Assert.Equal(listExpected, list); } [Fact] void RemoveFromTwoElementsStart_ToEmpty() { List list = new List() { new object(), new object() }; List listExpected = new List(); list.RemoveFrom(0); Assert.Equal(listExpected, list); } [Fact] void SingleDefaultOrErrorIListEmptyReturnsNull() { IList empty = new List(); object errorArgument = new object(); Action errorAction = (object argument) => { throw new InvalidOperationException(); }; Assert.Null(empty.SingleDefaultOrError(errorAction, errorArgument)); } [Fact] public void SingleDefaultOrErrorIListSingleReturns() { IList single = new List() { new object() }; object errorArgument = new object(); Action errorAction = (object argument) => { throw new InvalidOperationException(); }; Assert.Equal(single[0], single.SingleDefaultOrError(errorAction, errorArgument)); } [Fact] public void SingleDefaultOrErrorIListMultipleThrows() { IList multiple = new List() { new object(), new object() }; object errorArgument = new object(); Action errorAction = (object argument) => { Assert.Equal(errorArgument, argument); throw new InvalidOperationException(); }; Assert.Throws(() => multiple.SingleDefaultOrError(errorAction, errorArgument)); } [Fact] public void SingleOfTypeDefaultOrErrorIListNoMatchReturnsNull() { IList noMatch = new List() { new object(), new object() }; object errorArgument = new object(); Action errorAction = (object argument) => { throw new InvalidOperationException(); }; Assert.Null(noMatch.SingleOfTypeDefaultOrError(errorAction, errorArgument)); } [Fact] public void SingleOfTypeDefaultOrErrorIListOneMatchReturns() { IList singleMatch = new List() { new object(), "Match", new object() }; object errorArgument = new object(); Action errorAction = (object argument) => { throw new InvalidOperationException(); }; Assert.Equal("Match", singleMatch.SingleOfTypeDefaultOrError(errorAction, errorArgument)); } [Fact] public void SingleOfTypeDefaultOrErrorIListMultipleMatchesThrows() { IList multipleMatch = new List() { new object(), "Match1", new object(), "Match2" }; object errorArgument = new object(); Action errorAction = (object argument) => { Assert.Equal(errorArgument, argument); throw new InvalidOperationException(); }; Assert.Throws(() => multipleMatch.SingleOfTypeDefaultOrError(errorAction, errorArgument)); } [Fact] public void ToArrayWithoutNullsICollectionNoNullsCopies() { ICollection noNulls = new object[] { new object(), new object() }; object[] noNullsresult = noNulls.ToArrayWithoutNulls(); Assert.Equal(noNulls, noNullsresult); } [Fact] public void ToArrayWithoutNullsICollectionHasNullsRemovesNulls() { IList hasNulls = new List() { new object(), null, new object() }; object[] hasNullsResult = ((ICollection)hasNulls).ToArrayWithoutNulls(); Assert.Equal(2, hasNullsResult.Length); Assert.Equal(hasNulls[0], hasNullsResult[0]); Assert.Equal(hasNulls[2], hasNullsResult[1]); } [Fact] public void ToDictionaryFastArray2Element() { string[] input = new string[] {"AA", "BB"}; var expectedOutput = new Dictionary() { { "A", "AA"}, {"B", "BB"}}; Func keySelector = (string value) => value.Substring(1); var result = input.ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, result); Assert.Equal(StringComparer.OrdinalIgnoreCase, result.Comparer); } [Fact] public void ToDictionaryFastIListList2Element() { string[] input = new string[] {"AA", "BB"}; var expectedOutput = new Dictionary() { { "A", "AA"}, {"B", "BB"}}; Func keySelector = (string value) => value.Substring(1); List listInput = new List(input); var listResult = listInput.ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, listResult); Assert.Equal(StringComparer.OrdinalIgnoreCase, listResult.Comparer); } [Fact] public void ToDictionaryFastIListArray2Element() { string[] input = new string[] { "AA", "BB" }; var expectedOutput = new Dictionary() { { "A", "AA" }, { "B", "BB" } }; Func keySelector = (string value) => value.Substring(1); IList arrayAsList = input; var arrayResult = arrayAsList.ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, arrayResult); Assert.Equal(StringComparer.OrdinalIgnoreCase, arrayResult.Comparer); } [Fact] public void ToDictionaryFastIEnumerableArray2Element() { string[] input = new string[] {"AA", "BB"}; var expectedOutput = new Dictionary() { { "A", "AA"}, {"B", "BB"}}; Func keySelector = (string value) => value.Substring(1); var arrayResult = ((IEnumerable)input).ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, arrayResult); Assert.Equal(StringComparer.OrdinalIgnoreCase, arrayResult.Comparer); } [Fact] public void ToDictionaryFastIEnumerableList2Element() { string[] input = new string[] { "AA", "BB" }; var expectedOutput = new Dictionary() { { "A", "AA" }, { "B", "BB" } }; Func keySelector = (string value) => value.Substring(1); List listInput = new List(input); var listResult = ((IEnumerable)listInput).ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, listResult); Assert.Equal(StringComparer.OrdinalIgnoreCase, listResult.Comparer); } [Fact] public void ToDictionaryFastIEnumerableLinkedList2Element() { string[] input = new string[] { "AA", "BB" }; var expectedOutput = new Dictionary() { { "A", "AA" }, { "B", "BB" } }; Func keySelector = (string value) => value.Substring(1); LinkedList linkedListInput = new LinkedList(input); var enumerableResult = ((IEnumerable)linkedListInput).ToDictionaryFast(keySelector, StringComparer.OrdinalIgnoreCase); Assert.Equal(expectedOutput, enumerableResult); Assert.Equal(StringComparer.OrdinalIgnoreCase, enumerableResult.Comparer); } } } ================================================ FILE: test/Common/DictionaryExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net; using Microsoft.TestCommon; namespace System.Collections.Generic { public class DictionaryExtensionsTest { public static TheoryDataSet DictionaryValues { get { return new TheoryDataSet { "test", new string[] { "A", "B", "C" }, 8, new List {1, 2, 3}, 1D, (IEnumerable)new List { 1D, 2D, 3D }, new Uri("http://some.host"), Guid.NewGuid(), HttpStatusCode.NotImplemented, new HttpStatusCode[] { HttpStatusCode.Accepted, HttpStatusCode.Ambiguous, HttpStatusCode.BadGateway } }; } } [Fact] public void IsCorrectType() { Assert.Type.HasProperties(typeof(DictionaryExtensions), TypeAssert.TypeProperties.IsStatic | TypeAssert.TypeProperties.IsClass); } [Fact] public void RemoveFromDictionary_Args0_EvensRemoved() { Dictionary dictionary = new Dictionary(); object object1 = new object(); object object2 = new object(); object object3 = new object(); object object4 = new object(); dictionary.Add(object1, 1); dictionary.Add(object2, 2); dictionary.Add(object3, 3); dictionary.Add(object4, 4); Func, bool> removeAction = (KeyValuePair entry) => { // remove even values return (entry.Value % 2) == 0; }; dictionary.RemoveFromDictionary(removeAction); Assert.Equal(2, dictionary.Count); Assert.True(dictionary.ContainsKey(object1)); Assert.False(dictionary.ContainsKey(object2)); Assert.True(dictionary.ContainsKey(object3)); Assert.False(dictionary.ContainsKey(object4)); } [Fact] public void RemoveFromDictionary_Args1_EvensRemoved() { Dictionary dictionary = new Dictionary(); object object1 = new object(); object object2 = new object(); object object3 = new object(); object object4 = new object(); dictionary.Add(object1, 1); dictionary.Add(object2, 2); dictionary.Add(object3, 3); dictionary.Add(object4, 4); object expectedArgument = new object(); Func, object, bool> removeAction = (KeyValuePair entry, object arg) => { Assert.Equal(expectedArgument, arg); // remove even values return (entry.Value % 2) == 0; }; dictionary.RemoveFromDictionary(removeAction, expectedArgument); Assert.Equal(2, dictionary.Count); Assert.True(dictionary.ContainsKey(object1)); Assert.False(dictionary.ContainsKey(object2)); Assert.True(dictionary.ContainsKey(object3)); Assert.False(dictionary.ContainsKey(object4)); } [Fact] public void TryGetValueThrowsOnNullKey() { IDictionary dict = new Dictionary(); string value; Assert.ThrowsArgumentNull(() => dict.TryGetValue(null, out value), "key"); } [Fact] public void TryGetValueReturnsFalse() { // Arrange IDictionary dict = new Dictionary(); // Act string resultValue = null; bool result = dict.TryGetValue("notfound", out resultValue); // Assert Assert.False(result); Assert.Null(resultValue); } [Theory] [PropertyData("DictionaryValues")] public void TryGetValueReturnsTrue(T value) { // Arrange IDictionary dict = new Dictionary() { { "key", value } }; // Act T resultValue; bool result = DictionaryExtensions.TryGetValue(dict, "key", out resultValue); // Assert Assert.True(result); Assert.Equal(typeof(T), resultValue.GetType()); Assert.Equal(value, resultValue); } [Fact] public void FindKeysWithPrefixRecognizesRootChilden() { // Arrange IDictionary dict = new Dictionary() { { "[0]", 1 }, { "Name", 2 }, { "Address.Street", 3 }, { "", 4 } }; // Act List results = DictionaryExtensions.FindKeysWithPrefix(dict, "").Select(kvp => kvp.Value).ToList(); // Assert Assert.Equal(4, results.Count); Assert.Contains(1, results); Assert.Contains(2, results); Assert.Contains(3, results); Assert.Contains(4, results); } } } ================================================ FILE: test/Common/ErrorTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Web.Http { public class ErrorTest { [Fact] public void Format() { // Arrange string expected = "The formatted message"; // Act string actual = Error.Format("The {0} message", "formatted"); // Assert Assert.Equal(expected, actual); } } } ================================================ FILE: test/Common/HttpMethodHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http; using Microsoft.TestCommon; namespace System.Web.Http { public class HttpMethodHelperTest { public static TheoryDataSet CommonHttpMethods { get { return new TheoryDataSet { { "Get", HttpMethod.Get }, { "Post", HttpMethod.Post }, { "Put", HttpMethod.Put }, { "Delete", HttpMethod.Delete }, { "Head", HttpMethod.Head }, { "Options", HttpMethod.Options }, { "Trace", HttpMethod.Trace }, }; } } public static TheoryDataSet UncommonHttpMethods { get { return new TheoryDataSet { "Debug", "Patch", "Connect", "Random", "M-Get", }; } } [Fact] public void GetHttpMethod_ReturnsNullOnNullorEmpty() { Assert.Null(HttpMethodHelper.GetHttpMethod(null)); Assert.Null(HttpMethodHelper.GetHttpMethod(String.Empty)); } [Theory] [PropertyData("CommonHttpMethods")] public void GetHttpMethod_RetunsStaticResult(string method, HttpMethod expectedMethod) { Assert.Same(expectedMethod, HttpMethodHelper.GetHttpMethod(method)); Assert.Same(expectedMethod, HttpMethodHelper.GetHttpMethod(method.ToLowerInvariant())); Assert.Same(expectedMethod, HttpMethodHelper.GetHttpMethod(method.ToUpperInvariant())); } [Theory] [PropertyData("UncommonHttpMethods")] public void GetHttpMethod_RetunsNonStaticResult(string method) { Assert.Equal(method, HttpMethodHelper.GetHttpMethod(method).ToString()); } } } ================================================ FILE: test/Common/ListWrapperCollectionTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.TestCommon; namespace System.Collections.ObjectModel { public class ListWrapperCollectionTests { [Fact] public void ListWrapperCollection_ItemsList_HasSameContents() { // Arrange ListWrapperCollection listWrapper = new ListWrapperCollection(); // Act listWrapper.Add(new object()); listWrapper.Add(new object()); // Assert Assert.Equal(listWrapper, listWrapper.ItemsList); } [Fact] public void ListWrapperCollection_ItemsList_IsPassedInList() { // Arrange List list = new List() { new object(), new object() }; ListWrapperCollection listWrapper = new ListWrapperCollection(list); // Act & Assert Assert.Same(list, listWrapper.ItemsList); } } } ================================================ FILE: test/Common/PathHelpersTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web; using Microsoft.TestCommon; namespace System.Web.Test { public class PathHelpersTest { [Theory] [InlineData("foo.Bar", "bar")] [InlineData("foo.bar", "bar")] [InlineData(".bar", "bar")] public void EndsWithExtensionReturnsTrue(string path, string extension) { Assert.True(PathHelpers.EndsWithExtension(path, extension)); } [Theory] [InlineData("foo.Baz", "bar")] [InlineData("", "bar")] [InlineData("Bar", "bar")] [InlineData("fooBar", "bar")] public void EndsWithExtensionReturnsFalse(string path, string extension) { Assert.False(PathHelpers.EndsWithExtension(path, extension)); } } } ================================================ FILE: test/Common/PrefixContainerTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using Microsoft.TestCommon; namespace System.Web { [CLSCompliant(false)] public class PrefixContainerTests { [Fact] public void Constructor_GuardClauses() { // Act & assert Assert.ThrowsArgumentNull(() => new PrefixContainer(null), "values"); } [Fact] public void ContainsPrefix_GuardClauses() { // Arrange var container = new PrefixContainer(new string[0]); // Act & assert Assert.ThrowsArgumentNull(() => container.ContainsPrefix(null), "prefix"); } [Fact] public void ContainsPrefix_EmptyCollectionReturnsFalse() { // Arrange var container = new PrefixContainer(new string[0]); // Act & Assert Assert.False(container.ContainsPrefix("")); } [Fact] public void ContainsPrefix_ExactMatch() { // Arrange var container = new PrefixContainer(new[] { "Hello" }); // Act & Assert Assert.True(container.ContainsPrefix("Hello")); } [Fact] public void ContainsPrefix_MatchIsCaseInsensitive() { // Arrange var container = new PrefixContainer(new[] { "Hello" }); // Act & Assert Assert.True(container.ContainsPrefix("hello")); } [Fact] public void ContainsPrefix_MatchIsNotSimpleSubstringMatch() { // Arrange var container = new PrefixContainer(new[] { "Hello" }); // Act & Assert Assert.False(container.ContainsPrefix("He")); } [Fact] public void ContainsPrefix_NonEmptyCollectionReturnsTrueIfPrefixIsEmptyString() { // Arrange var container = new PrefixContainer(new[] { "Hello" }); // Act & Assert Assert.True(container.ContainsPrefix("")); } [Fact] public void ContainsPrefix_PrefixBoundaries() { // Arrange var container = new PrefixContainer(new[] { "Hello.There[0]" }); // Act & Assert Assert.True(container.ContainsPrefix("hello")); Assert.True(container.ContainsPrefix("hello.there")); Assert.True(container.ContainsPrefix("hello.there[0]")); Assert.False(container.ContainsPrefix("hello.there.0")); } [Theory] [InlineData("a")] [InlineData("a[d]")] [InlineData("c.b")] [InlineData("c.b.a")] public void ContainsPrefix_PositiveTests(string testValue) { // Arrange var container = new PrefixContainer(new[] { "a.b", "c.b.a", "a[d]", "a.c" }); // Act & Assert Assert.True(container.ContainsPrefix(testValue)); } [Theory] [InlineData("a.d")] [InlineData("b")] [InlineData("c.a")] [InlineData("c.b.a.a")] public void ContainsPrefix_NegativeTests(string testValue) { // Arrange var container = new PrefixContainer(new[] { "a.b", "c.b.a", "a[d]", "a.c" }); // Act & Assert Assert.False(container.ContainsPrefix(testValue)); } [Fact] public void ContainsPrefix_ShouldIdentifyCollectionWhenNonCollectionPropertyOccursOnBinarySearchBoundary() { // Arrange var container = new PrefixContainer(new[] { "foo.a", "foo.b", "foo.c", "foo.d", "foo.esSomethingElse", "foo.es[0].a", "foo.es[0].b", "foo.es[0].c", "foo.es[0].d", "foo.es[0].e" }); // Act & Assert Assert.True(container.ContainsPrefix("foo.es")); } [Fact] public void ContainsPrefix_ShouldIdentifyCollectionWhenNonCollectionPropertyDoesNotOccurOnBinarySearchBoundary() { // Arrange var container = new PrefixContainer(new[] { "foo.a", "foo.b", "foo.c", "foo.d", "foo.esSomethingElse", "foo.es[0].a", "foo.es[0].b", "foo.es[0].c" }); // Act & Assert Assert.True(container.ContainsPrefix("foo.es")); } [Fact] public void GetKeysFromPrefix_DotsNotation() { // Arrange var container = new PrefixContainer(new[] { "foo.bar.baz", "something.other", "foo.baz", "foot.hello", "fo.nothing", "foo" }); string prefix = "foo"; // Act IDictionary result = container.GetKeysFromPrefix(prefix); // Assert Assert.Equal(2, result.Count()); Assert.True(result.ContainsKey("bar")); Assert.True(result.ContainsKey("baz")); Assert.Equal("foo.bar", result["bar"]); Assert.Equal("foo.baz", result["baz"]); } [Fact] public void GetKeysFromPrefix_BracketsNotation() { // Arrange var container = new PrefixContainer(new[] { "foo[bar]baz", "something[other]", "foo[baz]", "foot[hello]", "fo[nothing]", "foo" }); string prefix = "foo"; // Act IDictionary result = container.GetKeysFromPrefix(prefix); // Assert Assert.Equal(2, result.Count()); Assert.True(result.ContainsKey("bar")); Assert.True(result.ContainsKey("baz")); Assert.Equal("foo[bar]", result["bar"]); Assert.Equal("foo[baz]", result["baz"]); } [Fact] public void GetKeysFromPrefix_MixedDotsAndBrackets() { // Arrange var container = new PrefixContainer(new[] { "foo[bar]baz", "something[other]", "foo.baz", "foot[hello]", "fo[nothing]", "foo" }); string prefix = "foo"; // Act IDictionary result = container.GetKeysFromPrefix(prefix); // Assert Assert.Equal(2, result.Count()); Assert.True(result.ContainsKey("bar")); Assert.True(result.ContainsKey("baz")); Assert.Equal("foo[bar]", result["bar"]); Assert.Equal("foo.baz", result["baz"]); } [Fact] public void GetKeysFromPrefix_AllValues() { // Arrange var container = new PrefixContainer(new[] { "foo[bar]baz", "something[other]", "foo.baz", "foot[hello]", "fo[nothing]", "foo" }); string prefix = ""; // Act IDictionary result = container.GetKeysFromPrefix(prefix); // Assert Assert.Equal(4, result.Count()); Assert.Equal("foo", result["foo"]); Assert.Equal("something", result["something"]); Assert.Equal("foot", result["foot"]); Assert.Equal("fo", result["fo"]); } [Fact] public void GetKeysFromPrefix_PrefixNotFound() { // Arrange var container = new PrefixContainer(new[] { "foo[bar]", "something[other]", "foo.baz", "foot[hello]", "fo[nothing]", "foo" }); string prefix = "notfound"; // Act IDictionary result = container.GetKeysFromPrefix(prefix); // Assert Assert.Empty(result); } } } ================================================ FILE: test/Common/Routing/DefaultInlineConstraintResolverTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if ASPNETWEBAPI using System.Web.Http.Routing.Constraints; #else using System.Web.Mvc.Routing.Constraints; #endif using Microsoft.TestCommon; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class DefaultInlineConstraintResolverTest { [Fact] public void ResolveConstraint_AlphaConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("alpha"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_BoolConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("bool"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_CompoundConstraintIsNotRegistered() { Assert.Null(new DefaultInlineConstraintResolver().ResolveConstraint("compound")); } [Fact] public void ResolveConstraint_DateTimeConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("datetime"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_DecimalConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("decimal"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_DoubleConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("double"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_EnumNameConstraintIsNotRegistered() { Assert.Null(new DefaultInlineConstraintResolver().ResolveConstraint("enumname")); } [Fact] public void ResolveConstraint_EnumValueConstraintIsNotRegistered() { Assert.Null(new DefaultInlineConstraintResolver().ResolveConstraint("enumvalue")); } [Fact] public void ResolveConstraint_FloatConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("float"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_GuidConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("guid"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_IntConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("int"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_LengthConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("length(5)"); var lengthRouteConstraint = Assert.IsType(constraint); Assert.Equal(5, lengthRouteConstraint.Length); } [Fact] public void ResolveConstraint_LengthRangeConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("length(5, 10)"); LengthRouteConstraint lengthConstraint = Assert.IsType(constraint); Assert.Equal(5, lengthConstraint.MinLength); Assert.Equal(10, lengthConstraint.MaxLength); } [Fact] public void ResolveConstraint_LongRangeConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("long"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_MaxConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("max(10)"); var maxRouteConstraint = Assert.IsType(constraint); Assert.Equal(10, maxRouteConstraint.Max); } [Fact] public void ResolveConstraint_MaxLengthConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("maxlength(10)"); var maxLengthRouteConstraint = Assert.IsType(constraint); Assert.Equal(10, maxLengthRouteConstraint.MaxLength); } [Fact] public void ResolveConstraint_MinConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("min(3)"); var minRouteConstraint = Assert.IsType(constraint); Assert.Equal(3, minRouteConstraint.Min); } [Fact] public void ResolveConstraint_MinLengthConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("minlength(3)"); var minLengthRouteConstraint = Assert.IsType(constraint); Assert.Equal(3, minLengthRouteConstraint.MinLength); } [Fact] public void ResolveConstraint_OptionalConstraintIsNotRegistered() { Assert.Null(new DefaultInlineConstraintResolver().ResolveConstraint("optional")); } [Fact] public void ResolveConstraint_RangeConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("range(5, 10)"); RangeRouteConstraint rangeConstraint = Assert.IsType(constraint); Assert.Equal(5, rangeConstraint.Min); Assert.Equal(10, rangeConstraint.Max); } [Fact] public void ResolveConstraint_RegexConstraint() { var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("regex(abc,defg)"); RegexRouteConstraint regexConstraint = Assert.IsType(constraint); Assert.Equal("abc,defg", regexConstraint.Pattern); } [Fact] public void ResolveConstraint_IntConstraintWithArgument_Throws() { Assert.Throws( () => new DefaultInlineConstraintResolver().ResolveConstraint("int(5)"), "Could not find a constructor for constraint type 'IntRouteConstraint' with the following number of parameters: 1."); } [Fact] public void ResolveConstraint_SupportsCustomConstraints() { var resolver = new DefaultInlineConstraintResolver(); resolver.ConstraintMap.Add("custom", typeof(IntRouteConstraint)); var constraint = resolver.ResolveConstraint("custom"); Assert.IsType(constraint); } [Fact] public void ResolveConstraint_CustomConstraintThatDoesNotImplementouteConstraintInterfact_Throws() { var resolver = new DefaultInlineConstraintResolver(); resolver.ConstraintMap.Add("custom", typeof(string)); Assert.Throws( () => resolver.ResolveConstraint("custom"), #if ASPNETWEBAPI "The constraint type 'String' which is mapped to constraint key 'custom' must implement the IHttpRouteConstraint interface."); #else "The constraint type 'String' which is mapped to constraint key 'custom' must implement the IRouteConstraint interface."); #endif } } } ================================================ FILE: test/Common/Routing/DirectRouteBuilderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; #if ASPNETWEBAPI using System.Net.Http; using System.Web.Http.Routing.Constraints; #else using System.Web.Mvc.Routing.Constraints; #endif using Microsoft.TestCommon; using Moq; #if ASPNETWEBAPI using TActionDescriptor = System.Web.Http.Controllers.HttpActionDescriptor; using TParsedRoute = System.Web.Http.Routing.HttpParsedRoute; using TRouteValueDictionary = System.Web.Http.Routing.HttpRouteValueDictionary; #else using TActionDescriptor = System.Web.Mvc.ActionDescriptor; using TParsedRoute = System.Web.Mvc.Routing.ParsedRoute; using TRouteValueDictionary = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class DirectRouteBuilderTests { [Fact] public void CreateRoute_ValidatesConstraintType_TRouteConstraint() { // Arrange var actions = GetActions(); var builder = new DirectRouteBuilder(actions, targetIsAction: true); var constraint = new AlphaRouteConstraint(); var constraints = new TRouteValueDictionary(); constraints.Add("custom", constraint); builder.Constraints = constraints; // Act var routeEntry = builder.Build(); // Assert Assert.NotNull(routeEntry.Route.Constraints["custom"]); } [Fact] public void BuildRoute_ValidatesConstraintType_StringRegex() { // Arrange var actions = GetActions(); var builder = new DirectRouteBuilder(actions, targetIsAction: true); var constraint = "product|products"; var constraints = new TRouteValueDictionary(); constraints.Add("custom", constraint); builder.Constraints = constraints; // Act var routeEntry = builder.Build(); // Assert Assert.NotNull(routeEntry.Route.Constraints["custom"]); } [Fact] public void BuildRoute_ValidatesConstraintType_InvalidType() { // Arrange var actions = GetActions(); var builder = new DirectRouteBuilder(actions, targetIsAction: true); var constraint = new Uri("http://localhost/"); var constraints = new TRouteValueDictionary(); constraints.Add("custom", constraint); builder.Constraints = constraints; builder.Template = "c/{id}"; #if ASPNETWEBAPI string expectedMessage = "The constraint entry 'custom' on the route with route template 'c/{id}' " + "must have a string value or be of a type which implements 'System.Web.Http.Routing.IHttpRouteConstraint'."; #else string expectedMessage = "The constraint entry 'custom' on the route with route template 'c/{id}' " + "must have a string value or be of a type which implements 'System.Web.Routing.IRouteConstraint'."; #endif // Act & Assert Assert.Throws(() => builder.Build(), expectedMessage); } [Fact] public void BuildRoute_ValidatesAllowedParameters() { // Arrange var actions = GetActions(); var builder = new MockDirectRouteBuilder(actions, targetIsAction: true); builder.Template = "{a}/{b}"; // Act RouteEntry entry = builder.Build(); // Assert Assert.NotNull(entry); Assert.Equal(1, builder.TimesValidateParametersCalled); } [Theory] [InlineData("{controller}", true)] [InlineData("{controller}", false)] [InlineData("{z}-abc-{controller}", true)] public void BuildRoute_ControllerParameterNotAllowed(string template, bool targetIsAction) { // Arrange var actions = GetActions(); var expectedMessage = "A direct route cannot use the parameter 'controller'. " + "Specify a literal path in place of this parameter to create a route to a controller."; var builder = new MockDirectRouteBuilder(actions, targetIsAction); builder.Template = template; // Act & Assert Assert.Throws(() => builder.Build(), expectedMessage); Assert.Equal(1, builder.TimesValidateParametersCalled); } [Fact] public void BuildRoute_ActionParameterAllowed_OnControllerRoute() { // Arrange var actions = GetActions(); var builder = new MockDirectRouteBuilder(actions, targetIsAction: false); builder.Template = "{a}/{action}"; // Act RouteEntry entry = builder.Build(); // Assert Assert.NotNull(entry); Assert.Equal(1, builder.TimesValidateParametersCalled); } [Theory] [InlineData("{action}")] [InlineData("api/yy-{action}")] public void BuildRoute_ActionNotAllowed_OnActionRoute(string template) { // Arrange var actions = GetActions(); var expectedMessage = "A direct route for an action method cannot use the parameter 'action'. " + "Specify a literal path in place of this parameter to create a route to the action."; var builder = new MockDirectRouteBuilder(actions, targetIsAction: true); builder.Template = template; // Act & Assert Assert.Throws(() => builder.Build(), expectedMessage); Assert.Equal(1, builder.TimesValidateParametersCalled); } #if ASPNETWEBAPI private IReadOnlyCollection GetActions() { var actions = new List() { new Mock().Object, }; return actions.AsReadOnly(); } #else private IReadOnlyCollection GetActions() { var action = new Mock(); action.SetupGet(a => a.ControllerDescriptor).Returns(new Mock().Object); var actions = new List() { action.Object, }; return actions.AsReadOnly(); } #endif private class MockDirectRouteBuilder : DirectRouteBuilder { public MockDirectRouteBuilder(IReadOnlyCollection actionDescriptors, bool targetIsAction) : base(actionDescriptors, targetIsAction) { } public int TimesValidateParametersCalled { get; private set; } internal override void ValidateParameters(TParsedRoute parsedRoute) { TimesValidateParametersCalled++; base.ValidateParameters(parsedRoute); } } } } ================================================ FILE: test/Common/Routing/InlineRouteTemplateParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; #if ASPNETWEBAPI using System.Web.Http.Routing.Constraints; #else using System.Web.Mvc.Routing.Constraints; using System.Web.Routing; #endif using Microsoft.TestCommon; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class InlineRouteTemplateParserTests { #if ASPNETWEBAPI private static readonly RouteParameter OptionalParameter = RouteParameter.Optional; #else private static readonly UrlParameter OptionalParameter = UrlParameter.Optional; #endif [Fact] public void ParseRouteTemplate_ChainedConstraintAndDefault() { var result = Act(@"hello/{param:int=111111}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal("111111", result.Defaults["param"]); Assert.IsType(result.Constraints["param"]); } [Fact] public void ParseRouteTemplate_ChainedConstraintWithArgumentsAndDefault() { var result = Act(@"hello/{param:regex(\d+)=111111}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal("111111", result.Defaults["param"]); var regexRouteConstraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\d+", regexRouteConstraint.Pattern); } [Fact] public void ParseRouteTemplate_ChainedConstraintAndOptional() { var result = Act(@"hello/{param:int?}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal(OptionalParameter, result.Defaults["param"]); var constraint = Assert.IsType(result.Constraints["param"]); Assert.IsType(constraint.InnerConstraint); } [Fact] public void ParseRouteTemplate_ChainedConstraintWithArgumentsAndOptional() { var result = Act(@"hello/{param:regex(\d+)?}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal(OptionalParameter, result.Defaults["param"]); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\d+", Assert.IsType(constraint.InnerConstraint).Pattern); } [Fact] public void ParseRouteTemplate_ChainedConstraints() { var result = Act(@"hello/{param:regex(\d+):regex(\w+)}"); Assert.Equal("hello/{param}", result.RouteUrl); CompoundRouteConstraint constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\d+", Assert.IsType(constraint.Constraints.ElementAt(0)).Pattern); Assert.Equal(@"\w+", Assert.IsType(constraint.Constraints.ElementAt(1)).Pattern); } [Fact] public void ParseRouteTemplate_Constraint() { var result = Act(@"hello/{param:regex(\d+)}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\d+", constraint.Pattern); } [Fact] public void ParseRouteTemplate_ConstraintsDefaultsAndOptionalsInMultipleSections() { var result = Act(@"some/url-{p1:alpha:length(3)=hello}/{p2=abc}/{p3?}"); Assert.Equal("some/url-{p1}/{p2}/{p3}", result.RouteUrl); Assert.Equal("hello", result.Defaults["p1"]); Assert.Equal("abc", result.Defaults["p2"]); Assert.Equal(OptionalParameter, result.Defaults["p3"]); CompoundRouteConstraint constraint = Assert.IsType(result.Constraints["p1"]); Assert.IsType(constraint.Constraints.ElementAt(0)); Assert.IsType(constraint.Constraints.ElementAt(1)); } [Fact] public void ParseRouteTemplate_NoTokens() { var result = Act("hello/world"); Assert.Equal("hello/world", result.RouteUrl); } [Fact] public void ParseRouteTemplate_OptionalParam() { var result = Act("hello/{param?}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal(OptionalParameter, result.Defaults["param"]); } [Fact] public void ParseRouteTemplate_ParamDefault() { var result = Act("hello/{param=world}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.Equal("world", result.Defaults["param"]); } [Fact] public void ParseRouteTemplate_RegexConstraintWithClosingBraceInPattern() { var result = Act(@"hello/{param:regex(\})}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\}", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithClosingParenInPattern() { var result = Act(@"hello/{param:regex(\))}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\)", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithColonInPattern() { var result = Act(@"hello/{param:regex(:)}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@":", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithCommaInPattern() { var result = Act(@"hello/{param:regex(\w,\w)}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\w,\w", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithEqualsSignInPattern() { var result = Act(@"hello/{param:regex(=)}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.DoesNotContain("param", result.Defaults.Keys); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"=", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithOpenBraceInPattern() { var result = Act(@"hello/{param:regex(\{)}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\{", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithOpenParenInPattern() { var result = Act(@"hello/{param:regex(\()}"); Assert.Equal("hello/{param}", result.RouteUrl); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\(", constraint.Pattern); } [Fact] public void ParseRouteTemplate_RegexConstraintWithQuestionMarkInPattern() { var result = Act(@"hello/{param:regex(\?)}"); Assert.Equal("hello/{param}", result.RouteUrl); Assert.DoesNotContain("param", result.Defaults.Keys); var constraint = Assert.IsType(result.Constraints["param"]); Assert.Equal(@"\?", constraint.Pattern); } private ParseResult Act(string template) { var result = new ParseResult(); #if ASPNETWEBAPI result.Constraints = new HttpRouteValueDictionary(); result.Defaults = new HttpRouteValueDictionary(); #else result.Constraints = new RouteValueDictionary(); result.Defaults = new RouteValueDictionary(); #endif result.RouteUrl = InlineRouteTemplateParser.ParseRouteTemplate(template, result.Defaults, result.Constraints, new DefaultInlineConstraintResolver()); return result; } struct ParseResult { public string RouteUrl; #if ASPNETWEBAPI public HttpRouteValueDictionary Defaults; public HttpRouteValueDictionary Constraints; #else public RouteValueDictionary Defaults; public RouteValueDictionary Constraints; #endif } } } ================================================ FILE: test/Common/Routing/RouteConstraintsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq.Expressions; #if ASPNETWEBAPI using System.Net.Http; using System.Web.Http.Routing.Constraints; #else using System.Web.Mvc.Routing.Constraints; using System.Web.Routing; #endif using Microsoft.TestCommon; using Moq; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class RouteConstraintsTests { [Theory] [InlineData(42, true)] [InlineData("42", true)] [InlineData(3.14, false)] [InlineData("43.567", false)] [InlineData("42a", false)] public void IntRouteConstraintTests(object parameterValue, bool expected) { var constraint = new IntRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(42, true)] [InlineData("42", true)] [InlineData("9223372036854775807", true)] [InlineData(3.14, false)] [InlineData("43.567", false)] [InlineData("42a", false)] public void LongRouteConstraintTests(object parameterValue, bool expected) { var constraint = new LongRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(@"^\d{3}-\d{3}-\d{4}$", "406-555-0123", true)] [InlineData(@"^\d{3}$", "1234", false)] public void RegexRouteConstraintTests(string pattern, string parameterValue, bool expected) { var constraint = new RegexRouteConstraint(pattern); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("alpha", true)] [InlineData("a1pha", false)] [InlineData("", true)] public void AlphaRouteConstraintTests(string parameterValue, bool expected) { var constraint = new AlphaRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(long.MinValue, long.MaxValue, 2, true)] [InlineData(3, 5, 4, true)] [InlineData(3, 5, 5, true)] [InlineData(3, 5, 3, true)] [InlineData(3, 5, 6, false)] [InlineData(3, 5, 2, false)] [InlineData(3, 1, 2, false)] public void RangeRouteConstraintTests(long min, long max, int parameterValue, bool expected) { var constraint = new RangeRouteConstraint(min, max); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, 4, true)] [InlineData(3, 3, true)] [InlineData(3, 2, false)] public void MinRouteConstraintTests(long min, int parameterValue, bool expected) { var constraint = new MinRouteConstraint(min); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, 2, true)] [InlineData(3, 3, true)] [InlineData(3, 4, false)] public void MaxRouteConstraintTests(long max, int parameterValue, bool expected) { var constraint = new MaxRouteConstraint(max); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, "1234", true)] [InlineData(3, "123", true)] [InlineData(3, "12", false)] [InlineData(3, "", false)] public void MinLengthRouteConstraintTests(int min, string parameterValue, bool expected) { var constraint = new MinLengthRouteConstraint(min); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, "", true)] [InlineData(3, "12", true)] [InlineData(3, "123", true)] [InlineData(3, "1234", false)] public void MaxLengthRouteConstraintTests(int min, string parameterValue, bool expected) { var constraint = new MaxLengthRouteConstraint(min); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, "123", true)] [InlineData(3, "1234", false)] public void LengthRouteConstraint_ExactLength_Tests(int length, string parameterValue, bool expected) { var constraint = new LengthRouteConstraint(length); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(3, 5, "12", false)] [InlineData(3, 5, "123", true)] [InlineData(3, 5, "1234", true)] [InlineData(3, 5, "12345", true)] [InlineData(3, 5, "123456", false)] public void LengthRouteConstraint_Range_Tests(int min, int max, string parameterValue, bool expected) { var constraint = new LengthRouteConstraint(min, max); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("12345678-1234-1234-1234-123456789012", false, true)] [InlineData("12345678-1234-1234-1234-123456789012", true, true)] [InlineData("12345678901234567890123456789012", false, true)] [InlineData("not-parseable-as-guid", false, false)] [InlineData(12, false, false)] public void GuidRouteConstraintTests(object parameterValue, bool parseBeforeTest, bool expected) { if (parseBeforeTest) { parameterValue = Guid.Parse(parameterValue.ToString()); } var constraint = new GuidRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("3.14", true)] [InlineData(3.14f, true)] [InlineData("not-parseable-as-float", false)] [InlineData(false, false)] [InlineData("1.79769313486232E+300", false)] public void FloatRouteConstraintTests(object parameterValue, bool expected) { var constraint = new FloatRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("3.14", true)] [InlineData(3.14f, true)] [InlineData("1.79769313486232E+300", true)] [InlineData("not-parseable-as-double", false)] [InlineData(false, false)] public void DoubleRouteConstraintTests(object parameterValue, bool expected) { var constraint = new DoubleRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("3.14", true)] [InlineData("9223372036854775808.9223372036854775808", true)] [InlineData("1.79769313486232E+300", false)] [InlineData("not-parseable-as-decimal", false)] [InlineData(false, false)] public void DecimalRouteConstraintTests(object parameterValue, bool expected) { var constraint = new DecimalRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("12/25/2009", true)] [InlineData("12/25/2009 11:45:00 PM", true)] [InlineData("11:45:00 PM", true)] [InlineData("2009-05-12T11:45:00Z", true)] [InlineData("not-parseable-as-date", false)] [InlineData(false, false)] public void DateTimeRouteConstraint(object parameterValue, bool expected) { var constraint = new DateTimeRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData("true", true)] [InlineData("false", true)] [InlineData(true, true)] [InlineData(false, true)] [InlineData(1, false)] [InlineData("not-parseable-as-bool", false)] public void BoolRouteConstraint(object parameterValue, bool expected) { var constraint = new BoolRouteConstraint(); var actual = TestValue(constraint, parameterValue); Assert.Equal(expected, actual); } [Theory] [InlineData(null, false, true)] [InlineData("pass", true, true)] [InlineData("fail", true, false)] public void OptionalRouteConstraintTests(object parameterValue, bool shouldCallInner, bool expected) { // Arrange var inner = MockConstraintWithResult((string)parameterValue != "fail"); // Act var constraint = new OptionalRouteConstraint(inner.Object); #if ASPNETWEBAPI var optionalParameter = RouteParameter.Optional; #else var optionalParameter = UrlParameter.Optional; #endif var actual = TestValue(constraint, parameterValue ?? optionalParameter, route => { route.Defaults.Add("fake", optionalParameter); }); // Assert Assert.Equal(expected, actual); var timeMatchShouldHaveBeenCalled = shouldCallInner ? Times.Once() : Times.Never(); AssertMatchWasCalled(inner, timeMatchShouldHaveBeenCalled); } [Theory] [InlineData(true, true, true)] [InlineData(true, false, false)] [InlineData(false, true, false)] [InlineData(false, false, false)] public void CompoundRouteConstraintTests(bool inner1Result, bool inner2Result, bool expected) { // Arrange var inner1 = MockConstraintWithResult(inner1Result); var inner2 = MockConstraintWithResult(inner2Result); // Act var constraint = new CompoundRouteConstraint(new[] { inner1.Object, inner2.Object }); var actual = TestValue(constraint, null); // Assert Assert.Equal(expected, actual); } #if ASPNETWEBAPI static Expression> ConstraintMatchMethodExpression = c => c.Match(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny()); private static Mock MockConstraintWithResult(bool result) { var mock = new Mock(); mock.Setup(ConstraintMatchMethodExpression) .Returns(result) .Verifiable(); return mock; } private static void AssertMatchWasCalled(Mock mock, Times times) { mock.Verify(ConstraintMatchMethodExpression, times); } private static bool TestValue(IHttpRouteConstraint constraint, object value, Action routeConfig = null) { HttpRequestMessage httpRequestMessage = new HttpRequestMessage(); HttpRoute httpRoute = new HttpRoute(); if (routeConfig != null) { routeConfig(httpRoute); } const string parameterName = "fake"; HttpRouteValueDictionary values = new HttpRouteValueDictionary { { parameterName, value } }; const HttpRouteDirection httpRouteDirection = HttpRouteDirection.UriResolution; return constraint.Match(httpRequestMessage, httpRoute, parameterName, values, httpRouteDirection); } #else static Expression> ConstraintMatchMethodExpression = c => c.Match(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()); private static Mock MockConstraintWithResult(bool result) { var mock = new Mock(); mock.Setup(ConstraintMatchMethodExpression) .Returns(result) .Verifiable(); return mock; } private static void AssertMatchWasCalled(Mock mock, Times times) { mock.Verify(ConstraintMatchMethodExpression, times); } private static bool TestValue(IRouteConstraint constraint, object value, Action routeConfig = null) { var context = new Mock(); Route route = new Route("", null); route.Defaults = new RouteValueDictionary(); if (routeConfig != null) { routeConfig(route); } const string parameterName = "fake"; RouteValueDictionary values = new RouteValueDictionary { { parameterName, value } }; const RouteDirection routeDirection = RouteDirection.IncomingRequest; return constraint.Match(context.Object, route, parameterName, values, routeDirection); } #endif } } ================================================ FILE: test/Common/Routing/RouteFactoryAttributeTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.Contracts; #if ASPNETWEBAPI using System.Web.Http.Controllers; #endif using Microsoft.TestCommon; using Moq; #if ASPNETWEBAPI using TActionDescriptor = System.Web.Http.Controllers.HttpActionDescriptor; using TRoute = System.Web.Http.Routing.IHttpRoute; using TRouteDictionary = System.Collections.Generic.IDictionary; using TRouteDictionaryConcrete = System.Web.Http.Routing.HttpRouteValueDictionary; #else using TActionDescriptor = System.Web.Mvc.ActionDescriptor; using TRoute = System.Web.Routing.Route; using TRouteDictionary = System.Web.Routing.RouteValueDictionary; using TRouteDictionaryConcrete = System.Web.Routing.RouteValueDictionary; #endif #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class RouteFactoryAttributeTests { [Fact] public void TemplateGet_ReturnsSpecifiedInstance() { // Arrange string expectedTemplate = "RouteTemplate"; RouteFactoryAttribute product = CreateProductUnderTest(expectedTemplate); // Act string template = product.Template; // Assert Assert.Same(expectedTemplate, template); } [Fact] public void NameGet_ReturnsNull() { // Arrange RouteFactoryAttribute product = CreateProductUnderTest(); // Act string name = product.Name; // Assert Assert.Null(name); } [Fact] public void OrderGet_ReturnsZero() { // Arrange RouteFactoryAttribute product = CreateProductUnderTest(); // Act int order = product.Order; // Assert Assert.Equal(0, order); } [Fact] public void ConstraintsGet_ReturnsNull() { // Arrange RouteFactoryAttribute product = CreateProductUnderTest(); // Act TRouteDictionary constraints = product.Constraints; // Assert Assert.Null(constraints); } [Fact] public void CreateRoute_DelegatesToContextCreateBuilderBuild() { // Arrange string expectedTemplate = "RouteTemplate"; IDirectRouteFactory product = CreateProductUnderTest(expectedTemplate); RouteEntry expectedEntry = CreateEntry(); IDirectRouteBuilder builder = CreateBuilder(() => expectedEntry); DirectRouteFactoryContext context = CreateContext((template) => template == expectedTemplate ? builder : new DirectRouteBuilder(new TActionDescriptor[0], targetIsAction: true)); // Act RouteEntry entry = product.CreateRoute(context); // Assert Assert.Same(expectedEntry, entry); } [Fact] public void CreateRoute_IfContextIsNull_Throws() { // Arrange DirectRouteFactoryContext context = null; IDirectRouteFactory product = CreateProductUnderTest(); // Act & Assert Assert.ThrowsArgumentNull(() => product.CreateRoute(context), "context"); } [Fact] public void CreateRoute_UsesNamePropertyWhenBuilding() { // Arrange string expectedName = "RouteName"; RouteFactoryAttribute product = CreateProductUnderTest(); product.Name = expectedName; string name = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { name = builder.Name; return null; }); DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(expectedName, name); } [Fact] public void CreateRoute_UsesOrderPropertyWhenBuilding() { // Arrange int expectedOrder = 123; RouteFactoryAttribute product = CreateProductUnderTest(); product.Order = expectedOrder; int order = 0; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { order = builder.Order; return null; }); DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Equal(expectedOrder, order); } [Fact] public void CreateRoute_IfBuilderDefaultsIsNull_UsesDefaultsPropertyWhenBuilding() { // Arrange TRouteDictionary expectedDefaults = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Defaults).Returns(expectedDefaults); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary defaults = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { defaults = builder.Defaults; return null; }); Assert.Null(builder.Defaults); // Guard DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(expectedDefaults, defaults); } [Fact] public void CreateRoute_IfBuilderDefaultsIsNotNull_UpdatesDefaultsFromPropertyWhenBuilding() { // Arrange TRouteDictionary existingDefaults = new TRouteDictionaryConcrete(); string existingDefaultKey = "ExistingDefaultKey"; object existingDefaultValue = "ExistingDefault"; existingDefaults.Add(existingDefaultKey, existingDefaultValue); string conflictingDefaultKey = "ConflictingDefaultKey"; object oldConflictingDefaultValue = "OldConflictingDefault"; existingDefaults.Add(conflictingDefaultKey, oldConflictingDefaultValue); TRouteDictionary additionalDefaults = new TRouteDictionaryConcrete(); string additionalDefaultKey = "NewDefaultKey"; string additionalDefaultValue = "NewDefault"; additionalDefaults.Add(additionalDefaultKey, additionalDefaultValue); string newConflictingDefaultValue = "NewConflictingDefault"; additionalDefaults.Add(conflictingDefaultKey, newConflictingDefaultValue); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Defaults).Returns(additionalDefaults); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary defaults = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { defaults = builder.Defaults; return null; }); builder.Defaults = existingDefaults; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingDefaults, defaults); Assert.Equal(3, defaults.Count); Assert.True(defaults.ContainsKey(existingDefaultKey)); Assert.Same(existingDefaultValue, defaults[existingDefaultKey]); Assert.True(defaults.ContainsKey(conflictingDefaultKey)); Assert.Same(newConflictingDefaultValue, defaults[conflictingDefaultKey]); Assert.True(defaults.ContainsKey(additionalDefaultKey)); Assert.Same(additionalDefaultValue, defaults[additionalDefaultKey]); } [Fact] public void CreateRoute_IfBuilderConstraintsIsNotNullAndDefaultsPropertyIsNull_UsesBuilderDefaults() { // Arrange TRouteDictionary existingDefaults = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Defaults).Returns((TRouteDictionary)null); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary defaults = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { defaults = builder.Defaults; return null; }); builder.Defaults = existingDefaults; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingDefaults, defaults); } [Fact] public void CreateRoute_IfBuilderConstraintsIsNull_UsesConstraintsPropertyWhenBuilding() { // Arrange TRouteDictionary expectedConstraints = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Constraints).Returns(expectedConstraints); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary constraints = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { constraints = builder.Constraints; return null; }); Assert.Null(builder.Constraints); // Guard DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(expectedConstraints, constraints); } [Fact] public void CreateRoute_IfBuilderConstraintsIsNotNull_UpdatesConstraintsFromPropertyWhenBuilding() { // Arrange TRouteDictionary existingConstraints = new TRouteDictionaryConcrete(); string existingConstraintKey = "ExistingConstraintKey"; object existingConstraintValue = "ExistingConstraint"; existingConstraints.Add(existingConstraintKey, existingConstraintValue); string conflictingConstraintKey = "ConflictingConstraintKey"; object oldConflictingConstraintValue = "OldConflictingConstraint"; existingConstraints.Add(conflictingConstraintKey, oldConflictingConstraintValue); TRouteDictionary additionalConstraints = new TRouteDictionaryConcrete(); string additionalConstraintKey = "NewConstraintKey"; string additionalConstraintValue = "NewConstraint"; additionalConstraints.Add(additionalConstraintKey, additionalConstraintValue); string newConflictingConstraintValue = "NewConflictingConstraint"; additionalConstraints.Add(conflictingConstraintKey, newConflictingConstraintValue); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Constraints).Returns(additionalConstraints); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary constraints = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { constraints = builder.Constraints; return null; }); builder.Constraints = existingConstraints; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingConstraints, constraints); Assert.Equal(3, constraints.Count); Assert.True(constraints.ContainsKey(existingConstraintKey)); Assert.Same(existingConstraintValue, constraints[existingConstraintKey]); Assert.True(constraints.ContainsKey(conflictingConstraintKey)); Assert.Same(newConflictingConstraintValue, constraints[conflictingConstraintKey]); Assert.True(constraints.ContainsKey(additionalConstraintKey)); Assert.Same(additionalConstraintValue, constraints[additionalConstraintKey]); } [Fact] public void CreateRoute_IfBuilderConstraintsIsNotNullAndConstraintsPropertyIsNull_UsesBuilderConstraints() { // Arrange TRouteDictionary existingConstraints = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.Constraints).Returns((TRouteDictionary)null); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary constraints = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { constraints = builder.Constraints; return null; }); builder.Constraints = existingConstraints; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingConstraints, constraints); } [Fact] public void CreateRoute_IfBuilderDataTokensIsNull_UsesDataTokensPropertyWhenBuilding() { // Arrange TRouteDictionary expectedDataTokens = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.DataTokens).Returns(expectedDataTokens); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary dataTokens = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { dataTokens = builder.DataTokens; return null; }); Assert.Null(builder.DataTokens); // Guard DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(expectedDataTokens, dataTokens); } [Fact] public void CreateRoute_IfBuilderDataTokensIsNotNull_UpdatesDataTokensFromPropertyWhenBuilding() { // Arrange TRouteDictionary existingDataTokens = new TRouteDictionaryConcrete(); string existingDataTokenKey = "ExistingDataTokenKey"; object existingDataTokenValue = "ExistingDataToken"; existingDataTokens.Add(existingDataTokenKey, existingDataTokenValue); string conflictingDataTokenKey = "ConflictingDataTokenKey"; object oldConflictingDataTokenValue = "OldConflictingDataToken"; existingDataTokens.Add(conflictingDataTokenKey, oldConflictingDataTokenValue); TRouteDictionary additionalDataTokens = new TRouteDictionaryConcrete(); string additionalDataTokenKey = "NewDataTokenKey"; string additionalDataTokenValue = "NewDataToken"; additionalDataTokens.Add(additionalDataTokenKey, additionalDataTokenValue); string newConflictingDataTokenValue = "NewConflictingDataToken"; additionalDataTokens.Add(conflictingDataTokenKey, newConflictingDataTokenValue); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.DataTokens).Returns(additionalDataTokens); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary dataTokens = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { dataTokens = builder.DataTokens; return null; }); builder.DataTokens = existingDataTokens; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingDataTokens, dataTokens); Assert.Equal(3, dataTokens.Count); Assert.True(dataTokens.ContainsKey(existingDataTokenKey)); Assert.Same(existingDataTokenValue, dataTokens[existingDataTokenKey]); Assert.True(dataTokens.ContainsKey(conflictingDataTokenKey)); Assert.Same(newConflictingDataTokenValue, dataTokens[conflictingDataTokenKey]); Assert.True(dataTokens.ContainsKey(additionalDataTokenKey)); Assert.Same(additionalDataTokenValue, dataTokens[additionalDataTokenKey]); } [Fact] public void CreateRoute_IfBuilderDataTokensIsNotNullAndDataTokensPropertyIsNull_UsesBuilderDataTokens() { // Arrange TRouteDictionary existingDataTokens = new TRouteDictionaryConcrete(); Mock productMock = CreateProductUnderTestMock(); productMock.SetupGet(p => p.DataTokens).Returns((TRouteDictionary)null); IDirectRouteFactory product = productMock.Object; RouteEntry expectedEntry = CreateEntry(); TRouteDictionary dataTokens = null; IDirectRouteBuilder builder = null; builder = CreateBuilder(() => { dataTokens = builder.DataTokens; return null; }); builder.DataTokens = existingDataTokens; DirectRouteFactoryContext context = CreateContext((i) => builder); // Act RouteEntry ignore = product.CreateRoute(context); // Assert Assert.Same(existingDataTokens, dataTokens); } [Fact] public void AttributeUsage_IsAsSpecified() { // Act AttributeUsageAttribute usage = (AttributeUsageAttribute)Attribute.GetCustomAttribute( typeof(RouteFactoryAttribute), typeof(AttributeUsageAttribute)); // Assert Assert.NotNull(usage); Assert.Equal(AttributeTargets.Class | AttributeTargets.Method, usage.ValidOn); Assert.False(usage.Inherited); Assert.True(usage.AllowMultiple); } private static IDirectRouteBuilder CreateBuilder(Func build) { return new LambdaDirectRouteBuilder(build); } private static DirectRouteFactoryContext CreateContext(Func createBuilder) { return new LambdaDirectRouteFactoryContext(createBuilder); } private static RouteEntry CreateEntry() { #if ASPNETWEBAPI TRoute route = new Mock(MockBehavior.Strict).Object; #else TRoute route = new Mock(MockBehavior.Strict, null, null).Object; #endif return new RouteEntry("IgnoreEntry", route); } private static RouteFactoryAttribute CreateProductUnderTest() { return CreateProductUnderTest("IgnoreTemplate"); } private static RouteFactoryAttribute CreateProductUnderTest(string template) { return CreateProductUnderTestMock(template).Object; } private static Mock CreateProductUnderTestMock() { return CreateProductUnderTestMock("IgnoreTemplate"); } private static Mock CreateProductUnderTestMock(string template) { Mock mock = new Mock(template); mock.CallBase = true; return mock; } private class LambdaDirectRouteFactoryContext : DirectRouteFactoryContext { private readonly Func _createBuilder; public LambdaDirectRouteFactoryContext(Func createBuilder) #if ASPNETWEBAPI : base(null, new TActionDescriptor[] { new Mock().Object }, new Mock(MockBehavior.Strict).Object, targetIsAction: true) #else : base(null, null, new TActionDescriptor[] { CreateStubActionDescriptor() }, new Mock(MockBehavior.Strict).Object, targetIsAction: true) #endif { Contract.Assert(createBuilder != null); _createBuilder = createBuilder; } internal override IDirectRouteBuilder CreateBuilderInternal(string template) { return _createBuilder.Invoke(template); } #if !ASPNETWEBAPI private static ActionDescriptor CreateStubActionDescriptor() { Mock mock = new Mock(); mock.Setup(m => m.ControllerDescriptor).Returns(new Mock().Object); return mock.Object; } #endif } private class LambdaDirectRouteBuilder : DirectRouteBuilder { private readonly Func _build; public LambdaDirectRouteBuilder(Func build) : base(new TActionDescriptor[0], targetIsAction: true) { Contract.Assert(build != null); _build = build; } public override RouteEntry Build() { return _build.Invoke(); } } } } ================================================ FILE: test/Common/Routing/RoutePrecedenceTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if !ASPNETWEBAPI using System.Web.Routing; #endif using Microsoft.TestCommon; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class RoutePrecedenceTests { [Theory] [InlineData("Employees/{id}", "Employees/{id}")] [InlineData("abc", "def")] [InlineData("{x:alpha}", "{x:int}")] public void Compute_IsEqual(string xTemplate, string yTemplate) { // Arrange & Act var xPrededence = Compute(xTemplate); var yPrededence = Compute(yTemplate); // Assert Assert.Equal(xPrededence, yPrededence); } [Theory] [InlineData("abc", "a{x}")] [InlineData("abc", "{x}c")] [InlineData("abc", "{x:int}")] [InlineData("abc", "{x}")] [InlineData("abc", "{*x}")] [InlineData("{x:int}", "{x}")] [InlineData("{x:int}", "{*x}")] [InlineData("a{x}", "{x}")] [InlineData("{x}c", "{x}")] [InlineData("a{x}", "{*x}")] [InlineData("{x}c", "{*x}")] [InlineData("{x}", "{*x}")] [InlineData("{*x:maxlength(10)}", "{*x}")] [InlineData("abc/def", "abc/{x:int}")] [InlineData("abc/def", "abc/{x}")] [InlineData("abc/def", "abc/{*x}")] [InlineData("abc/{x:int}", "abc/{x}")] [InlineData("abc/{x:int}", "abc/{*x}")] [InlineData("abc/{x}", "abc/{*x}")] [InlineData("{x}/{y:int}", "{x}/{y}")] public void Compute_IsLessThan(string xTemplate, string yTemplate) { // Arrange & Act var xPrededence = Compute(xTemplate); var yPrededence = Compute(yTemplate); // Assert Assert.True(xPrededence < yPrededence); } private static decimal Compute(string template) { DefaultInlineConstraintResolver resolver = new DefaultInlineConstraintResolver(); #if ASPNETWEBAPI HttpRouteValueDictionary defaults = new HttpRouteValueDictionary(); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); #else RouteValueDictionary defaults = new RouteValueDictionary(); RouteValueDictionary constraints = new RouteValueDictionary(); #endif string standardRouteTemplate = InlineRouteTemplateParser.ParseRouteTemplate(template, defaults, constraints, new DefaultInlineConstraintResolver()); var parsedRoute = RouteParser.Parse(standardRouteTemplate); return RoutePrecedence.Compute(parsedRoute, constraints); } } } ================================================ FILE: test/Common/Routing/SubRouteCollectionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #if !ASPNETWEBAPI using System.Web.Routing; #endif using Microsoft.TestCommon; using Moq; #if ASPNETWEBAPI namespace System.Web.Http.Routing #else namespace System.Web.Mvc.Routing #endif { public class SubRouteCollectionTest { #if ASPNETWEBAPI [Fact] public void SubRouteCollection_Throws_OnDuplicateNamedRoute_WebAPI() { // Arrange var collection = new SubRouteCollection(); var route1 = new HttpRoute("api/Person"); var route2 = new HttpRoute("api/Car"); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "api/Car" + Environment.NewLine + "api/Person"; // Act & Assert Assert.Throws(() => collection.Add(new RouteEntry("route", route2)), expectedError); } #else [Fact] public void SubRouteCollection_Throws_OnDuplicateNamedRoute_MVC() { // Arrange var collection = new SubRouteCollection(); var route1 = new Route("Home/Index", new Mock().Object); var route2 = new Route("Person/Index", new Mock().Object); collection.Add(new RouteEntry("route", route1)); var expectedError = "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" + "Duplicates:" + Environment.NewLine + "Person/Index" + Environment.NewLine + "Home/Index"; // Act & Assert Assert.Throws(() => collection.Add(new RouteEntry("route", route2)), expectedError); } #endif } } ================================================ FILE: test/Common/TaskHelpersExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Threading.Tasks { public class TaskHelpersExtensionsTest { // ---------------------------------------------------------------- // Task Task.CastToObject() [Fact, ForceGC] public async Task ConvertFromTaskOfStringShouldSucceed() { // Arrange var task = Task.FromResult("StringResult") .CastToObject(); // Act var result = await task; // Assert Assert.Equal(TaskStatus.RanToCompletion, task.Status); Assert.Equal("StringResult", (string)result); } [Fact, ForceGC] public async Task ConvertFromTaskOfIntShouldSucceed() { // Arrange var task = Task.FromResult(123) .CastToObject(); // Act var result = await task; // Assert Assert.Equal(TaskStatus.RanToCompletion, task.Status); Assert.Equal(123, (int)result); } [Fact, ForceGC] public async Task ConvertFromFaultedTaskOfObjectShouldBeHandled() { // Arrange var task = TaskHelpers.FromError(new InvalidOperationException()) .CastToObject(); // Act & Assert await Assert.ThrowsAsync(() => task); Assert.Equal(TaskStatus.Faulted, task.Status); } [Fact, ForceGC] public async Task ConvertFromCancelledTaskOfStringShouldBeHandled() { // Arrange var task = TaskHelpers.Canceled() .CastToObject(); // Act & Assert await Assert.ThrowsAsync(() => task); Assert.Equal(TaskStatus.Canceled, task.Status); } // ---------------------------------------------------------------- // Task Task.CastToObject() [Fact, ForceGC] public async Task ConvertFromTaskShouldSucceed() { // Arrange var task = TaskHelpers.Completed() .CastToObject(); // Act var result = await task; // Assert Assert.Equal(TaskStatus.RanToCompletion, task.Status); Assert.Null(result); } [Fact, ForceGC] public async Task ConvertFromFaultedTaskShouldBeHandled() { // Arrange var task = TaskHelpers.FromError(new InvalidOperationException()) .CastToObject(); // Act & Assert await Assert.ThrowsAsync(() => task); Assert.Equal(TaskStatus.Faulted, task.Status); } [Fact, ForceGC] public async Task ConvertFromCancelledTaskShouldBeHandled() { // Arrange var task = TaskHelpers.Canceled() .CastToObject(); // Act & Assert await Assert.ThrowsAsync(() => task); Assert.Equal(TaskStatus.Canceled, task.Status); } // ----------------------------------------------------------------- // bool Task.TryGetResult(Task, out TResult) [Fact, ForceGC] public void TryGetResult_CompleteTask_ReturnsTrueAndGivesResult() { // Arrange var task = Task.FromResult(42); // Act int value; bool result = task.TryGetResult(out value); // Assert Assert.True(result); Assert.Equal(42, value); } [Fact, ForceGC] public void TryGetResult_FaultedTask_ReturnsFalse() { // Arrange var task = TaskHelpers.FromError(new Exception()); // Act int value; bool result = task.TryGetResult(out value); // Assert Assert.False(result); var ex = task.Exception; // Observe the task exception } [Fact, ForceGC] public void TryGetResult_CanceledTask_ReturnsFalse() { // Arrange var task = TaskHelpers.Canceled(); // Act int value; bool result = task.TryGetResult(out value); // Assert Assert.False(result); } [Fact, ForceGC] public Task TryGetResult_IncompleteTask_ReturnsFalse() { // Arrange var incompleteTask = new Task(() => 42); // Act int value; bool result = incompleteTask.TryGetResult(out value); // Assert Assert.False(result); incompleteTask.Start(); return incompleteTask; // Make sure the task gets observed } } } ================================================ FILE: test/Common/TaskHelpersTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using Microsoft.TestCommon; namespace System.Threading.Tasks { public class TaskHelpersTest { // ----------------------------------------------------------------- // TaskHelpers.Canceled [Fact] public void Canceled_ReturnsCanceledTask() { Task result = TaskHelpers.Canceled(); Assert.NotNull(result); Assert.True(result.IsCanceled); } // ----------------------------------------------------------------- // TaskHelpers.Canceled [Fact] public void Canceled_Generic_ReturnsCanceledTask() { Task result = TaskHelpers.Canceled(); Assert.NotNull(result); Assert.True(result.IsCanceled); } // ----------------------------------------------------------------- // TaskHelpers.Completed [Fact] public void Completed_ReturnsCompletedTask() { Task result = TaskHelpers.Completed(); Assert.NotNull(result); Assert.Equal(TaskStatus.RanToCompletion, result.Status); } // ----------------------------------------------------------------- // TaskHelpers.FromError [Fact] public void FromError_ReturnsFaultedTaskWithGivenException() { var exception = new Exception(); Task result = TaskHelpers.FromError(exception); Assert.NotNull(result); Assert.True(result.IsFaulted); Assert.Same(exception, result.Exception.InnerException); } // ----------------------------------------------------------------- // TaskHelpers.FromError [Fact] public void FromError_Generic_ReturnsFaultedTaskWithGivenException() { var exception = new Exception(); Task result = TaskHelpers.FromError(exception); Assert.NotNull(result); Assert.True(result.IsFaulted); Assert.Same(exception, result.Exception.InnerException); } } } ================================================ FILE: test/Common/TypeExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using Microsoft.TestCommon; namespace System { public class TypeExtensionsTest { [Theory] [InlineData(typeof(int), false)] [InlineData(typeof(string), true)] [InlineData(typeof(DateTime), false)] [InlineData(typeof(int?), true)] [InlineData(typeof(IEnumerable), true)] [InlineData(typeof(int[]), true)] [InlineData(typeof(string[]), true)] public void IsNullable_Returns_ExpectedValue(Type type, bool expectedResult) { Assert.Equal(expectedResult, type.IsNullable()); } } } ================================================ FILE: test/Common/UriQueryUtilityTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.Net.Http.Formatting; using System.Text; using System.Web.Http; using Microsoft.TestCommon; namespace System.Net.Http { public class WebUtilityTest { public static TheoryDataSet UriQueryData { get { return UriQueryTestData.UriQueryData; } } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(WebUtility), TypeAssert.TypeProperties.IsStatic | TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void UrlEncode_ReturnsNull() { Assert.Null(WebUtility.UrlEncode(null)); } [Fact] public void UrlDecode_ReturnsNull() { Assert.Null(WebUtility.UrlDecode(null)); } [Fact] public void UrlDecode_ParsesEmptySegmentsCorrectly() { int iterations = 16; List segments = new List(); for (int index = 1; index < iterations; index++) { segments.Add("&"); string query = string.Join("", segments); NameValueCollection result = ParseQueryString(query); Assert.NotNull(result); // Because this is a NameValueCollection, the same name appears only once Assert.Single(result); // Values should be a comma separated list of empty strings string[] values = result[""].Split(new char[] { ',' }); // We expect length+1 segment as the final '&' counts as a segment Assert.Equal(index + 1, values.Length); foreach (var value in values) { Assert.Equal("", value); } } } [Theory] [InlineData("N", "N", "")] [InlineData("%26", "&", "")] [InlineData("foo=%u0026", "foo", "%u0026")] [PropertyData("UriQueryData")] public void UrlDecode_ParsesCorrectly(string segment, string resultName, string resultValue) { int iterations = 16; List segments = new List(); for (int index = 1; index < iterations; index++) { segments.Add(segment); string query = CreateQuery(segments.ToArray()); NameValueCollection result = ParseQueryString(query); Assert.NotNull(result); // Because this is a NameValueCollection, the same name appears only once Assert.Single(result); // Values should be a comma separated list of resultValue string[] values = result[resultName].Split(new char[] { ',' }); Assert.Equal(index, values.Length); foreach (var value in values) { Assert.Equal(resultValue, value); } } } private static string CreateQuery(params string[] segments) { StringBuilder buffer = new StringBuilder(); bool first = true; foreach (string segment in segments) { if (first) { first = false; } else { buffer.Append('&'); } buffer.Append(segment); } return buffer.ToString(); } private static NameValueCollection ParseQueryString(string query) { return new FormDataCollection(query).ReadAsNameValueCollection(); } } } ================================================ FILE: test/Directory.Build.props ================================================ false true v4.6.2 ================================================ FILE: test/Directory.Build.targets ================================================ ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/App.config ================================================  ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/DefaultFacebookClientProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Facebook; using Microsoft.AspNet.Facebook.Providers; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class DefaultFacebookClientProviderTest { [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull(() => new DefaultFacebookClientProvider(null), "configuration"); } [Fact] public void CreateClient_ReturnsClientWithAppIdAndAppSecret() { string appId = "654321"; string appSecret = "abcdefg123"; FacebookConfiguration config = new FacebookConfiguration { AppId = appId, AppSecret = appSecret }; DefaultFacebookClientProvider clientProvider = new DefaultFacebookClientProvider(config); FacebookClient client = clientProvider.CreateClient(); Assert.Equal(appId, client.AppId); Assert.Equal(appSecret, client.AppSecret); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/DefaultFacebookPermissionServiceTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNet.Facebook.Providers; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class DefaultFacebookPermissionServiceTest { [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull(() => new DefaultFacebookPermissionService(null), "configuration"); } [Fact] public void GetUserPermissions_ThrowsArgumentNullException() { FacebookConfiguration config = new FacebookConfiguration(); config.ClientProvider = new DefaultFacebookClientProvider(config); DefaultFacebookPermissionService permissionService = new DefaultFacebookPermissionService(config); Assert.ThrowsArgumentNull(() => permissionService.GetUserPermissions(null, "accessToken"), "userId"); Assert.ThrowsArgumentNull(() => permissionService.GetUserPermissions("userId", null), "accessToken"); } [Fact] public void GetUserPermissions_CallsGetOnFacebookClientWithExpectedPath() { LocalFacebookClient localClient = new LocalFacebookClient(); FacebookConfiguration config = MockHelpers.CreateConfiguration(localClient); DefaultFacebookPermissionService permissionService = new DefaultFacebookPermissionService(config); permissionService.GetUserPermissions("123456", "sampleAccessToken"); Assert.Equal("me/permissions", localClient.Path); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookAuthorizeAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookAuthorizeAttributeTest { [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull(() => new FacebookAuthorizeAttribute(null), "permissions"); } [Fact] public void Permissions_ReturnsExpectedValues() { string[] permissions = new[] { "email", "user_likes", "friends_likes" }; FacebookAuthorizeAttribute authorizeAttribute = new FacebookAuthorizeAttribute(permissions); HashSet permissionSet = new HashSet(permissions); Assert.True(permissionSet.SetEquals(authorizeAttribute.Permissions)); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookAuthorizeFilterHookTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Web; using System.Web.Mvc; using Microsoft.AspNet.Facebook.Authorization; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookAuthorizeFilterHookTest { [Theory] [InlineData("~/home/cannotcreatecookies", "https://apps.facebook.com/DefaultAppId/home/cannotcreatecookies")] [InlineData(null, "https://www.facebook.com/")] public void OnAuthorization_CannotCreateCookiesHookRedirectsToConfigValueOrDefault( string cannotCreateCookiesRedirectPath, string expectedRedirectPath) { // Arrange var config = BuildConfiguration("~/home/permissions", cannotCreateCookiesRedirectPath); var authorizeFilter = new FacebookAuthorizeFilter(config); var context = BuildSignedAuthorizationContext("http://contoso.com?__fb_mps=true", "email"); // Act authorizeFilter.OnAuthorization(context); var result = context.Result as JavaScriptRedirectResult; // Assert Assert.Equal(result.RedirectUrl.AbsoluteUri, new Uri(expectedRedirectPath).AbsoluteUri); } [Fact] public void OnAuthorization_OnlyTriggersCannotCreateCookiesHook() { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var context = BuildSignedAuthorizationContext("http://contoso.com?__fb_mps=true", "email"); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.True(authorizeFilter.CannotCreateCookiesHookTriggered); Assert.False(authorizeFilter.PermissionPromptHookTriggered); Assert.False(authorizeFilter.DeniedPermissionPromptHookTriggered); } [Theory] [InlineData("http://contoso.com?__fb_mps=true", "email", true)] [InlineData("http://contoso.com", "email", false)] [InlineData("http://contoso.com?__fb_mps=true", null, false)] public void OnAuthorization_TriggersCannotCreateCookiesHook(string requestUrl, string permission, bool expectedTrigger) { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var context = BuildSignedAuthorizationContext(requestUrl, permission); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(expectedTrigger, authorizeFilter.CannotCreateCookiesHookTriggered); } [Theory] [InlineData("http://contoso.com", "email", true)] [InlineData("http://contoso.com?error=access_denied", "email", false)] [InlineData("http://contoso.com?error=access_denied", null, false)] public void OnAuthorization_TriggersPreHookPriorToPermissionsDialog(string requestUrl, string permission, bool expectedTrigger) { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var context = BuildSignedAuthorizationContext(requestUrl, permission); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(expectedTrigger, authorizeFilter.PermissionPromptHookTriggered); } [Theory] [InlineData("http://contoso.com", "email", true)] [InlineData("http://contoso.com?error=access_denied", "email", false)] [InlineData("http://contoso.com?error=access_denied", null, false)] public void OnAuthorization_TriggersDeniedHook(string requestUrl, string permission, bool expectedTrigger) { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var persistedCookies = new HttpCookieCollection(); persistedCookies.Add( new HttpCookie( PermissionHelper.RequestedPermissionCookieName, permission ?? string.Empty)); var context = BuildSignedAuthorizationContext(requestUrl, permission, persistedCookies); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(expectedTrigger, authorizeFilter.DeniedPermissionPromptHookTriggered); } [Theory] [InlineData("http://contoso.com", "email", "email", true)] [InlineData("http://contoso.com", "email", "foo", false)] [InlineData("http://contoso.com?error=access_denied", "email", "email", false)] [InlineData("http://contoso.com?error=access_denied", "email", "foo", false)] [InlineData("http://contoso.com?error=access_denied", null, "foo", false)] public void OnAuthorization_TriggersDeniedHookWithRevokedPermissions(string requestUrl, string permission, string permissionInStatus, bool expectedTrigger) { var rawPermissionsStatus = new Dictionary { { "permission", permissionInStatus }, { "status", "declined" }, }; var data = new List>(new[] { rawPermissionsStatus }); // Arrange var config = BuildConfiguration("~/home/permissions", userPermissionsStatus: new PermissionsStatus(data)); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var context = BuildSignedAuthorizationContext(requestUrl, permission); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(expectedTrigger, authorizeFilter.DeniedPermissionPromptHookTriggered); } [Theory] [InlineData("http://contoso.com", "email", true)] [InlineData("http://contoso.com?error=access_denied", "email", false)] [InlineData("http://contoso.com?error=access_denied", null, false)] public void OnAuthorization_TriggersDeniedHookAfterPersistingRequestedPermissions(string requestUrl, string permission, bool expectedTrigger) { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomDefaultAuthorizeFilter(config); var context = BuildSignedAuthorizationContext(requestUrl, permission); // Act authorizeFilter.OnAuthorization(context); // Here we're acting like a browser and adding the responses cookies to the "next" request's cookies var responseCookies = context.HttpContext.Response.Cookies; foreach (var cookieName in responseCookies.AllKeys) { context.HttpContext.Request.Cookies.Add(responseCookies[cookieName]); } // Assert Assert.False(authorizeFilter.DeniedPermissionPromptHookTriggered); // Act 2 // We're making a "second" request essentially authorizeFilter.OnAuthorization(context); // Assert 2 Assert.Equal(expectedTrigger, authorizeFilter.DeniedPermissionPromptHookTriggered); } [Fact] public void OnAuthorization_CannotCreateCookiesHookNullFlows() { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomInvalidAuthorizeFilter(config); var context = BuildSignedAuthorizationContext("http://contoso.com?__fb_mps=true", "email"); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Null(context.Result); } [Fact] public void OnAuthorization_PreHookNullTreatedLikeIgnoreResult() { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomInvalidAuthorizeFilter(config); var context = BuildSignedAuthorizationContext("http://contoso.com", "email"); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Null(context.Result); } [Fact] public void OnAuthorization_DeniedHookNullTreatedLikeIgnoreResult() { // Arrange var config = BuildConfiguration("~/home/permissions"); var authorizeFilter = new CustomInvalidAuthorizeFilter(config); var persistedCookies = new HttpCookieCollection(); persistedCookies.Add( new HttpCookie( PermissionHelper.RequestedPermissionCookieName, "email")); var context = BuildSignedAuthorizationContext("http://contoso.com", "email", persistedCookies); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Null(context.Result); } [Fact] public void OnAuthorization_CannotCreateCookiesHookCustomActionResultIsContextsResult() { // Arrange var tempUrl = "http://contoso.com?__fb_mps=true"; var config = BuildConfiguration("~/home/permissions"); var cannotCreateCookiesHookResult = new RedirectResult(tempUrl); var authorizeFilter = new CustomReturningAuthorizeFilter(config, cannotCreateCookiesHookResult, new RedirectResult(tempUrl), new RedirectResult(tempUrl)); var context = BuildSignedAuthorizationContext(tempUrl, "email"); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(cannotCreateCookiesHookResult, context.Result); } [Fact] public void OnAuthorization_PreHookCustomActionResultIsContextsResult() { // Arrange var tempUrl = "http://contoso.com"; var config = BuildConfiguration("~/home/permissions"); var preHookResult = new RedirectResult(tempUrl); var authorizeFilter = new CustomReturningAuthorizeFilter(config, new RedirectResult(tempUrl), preHookResult, new RedirectResult(tempUrl)); var context = BuildSignedAuthorizationContext(tempUrl, "email"); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(preHookResult, context.Result); } [Fact] public void OnAuthorization_DeniedHookCustomActionResultIsContextsResult() { // Arrange var tempUrl = "http://contoso.com"; var config = BuildConfiguration("~/home/permissions"); var deniedHookResult = new RedirectResult(tempUrl); var authorizeFilter = new CustomReturningAuthorizeFilter(config, new RedirectResult(tempUrl), new RedirectResult(tempUrl), deniedHookResult); var persistedCookies = new HttpCookieCollection(); persistedCookies.Add( new HttpCookie( PermissionHelper.RequestedPermissionCookieName, "email")); var context = BuildSignedAuthorizationContext(tempUrl, "email", persistedCookies); // Act authorizeFilter.OnAuthorization(context); // Assert Assert.Equal(deniedHookResult, context.Result); } // Helper methods and classes private FacebookConfiguration BuildConfiguration(string authorizationRedirectPath, string cannotCreateCookiesRedirectPath = null, PermissionsStatus userPermissionsStatus = null) { var client = MockHelpers.CreateFacebookClient(); var permissionService = MockHelpers.CreatePermissionService(new[] { "" }, userPermissionsStatus); var config = MockHelpers.CreateConfiguration(client, permissionService); config.AuthorizationRedirectPath = authorizationRedirectPath; if (cannotCreateCookiesRedirectPath != null) { config.CannotCreateCookieRedirectPath = cannotCreateCookiesRedirectPath; } return config; } private AuthorizationContext BuildSignedAuthorizationContext(string requestUrl, string permission, HttpCookieCollection requestCookies = null) { var permissions = permission == null ? new string[0] : new string[] { permission }; var requestUri = new Uri(requestUrl); var context = new AuthorizationContext( MockHelpers.CreateControllerContext(new NameValueCollection { {"signed_request", "exampleSignedRequest"} }, HttpUtility.ParseQueryString(requestUri.Query), requestUri, requestCookies), MockHelpers.CreateActionDescriptor(new[] { new FacebookAuthorizeAttribute(permissions) })); return context; } private class CustomInvalidAuthorizeFilter : FacebookAuthorizeFilter { public CustomInvalidAuthorizeFilter(FacebookConfiguration config) : base(config) { } protected override void OnCannotCreateCookies(PermissionContext context) { context.Result = null; } protected override void OnPermissionPrompt(PermissionContext context) { context.Result = null; } protected override void OnDeniedPermissionPrompt(PermissionContext context) { context.Result = null; } } private class CustomDefaultAuthorizeFilter : FacebookAuthorizeFilter { public CustomDefaultAuthorizeFilter(FacebookConfiguration config) : base(config) { } public bool CannotCreateCookiesHookTriggered { get; private set; } public bool PermissionPromptHookTriggered { get; private set; } public bool DeniedPermissionPromptHookTriggered { get; private set; } protected override void OnCannotCreateCookies(PermissionContext context) { CannotCreateCookiesHookTriggered = true; base.OnCannotCreateCookies(context); } protected override void OnPermissionPrompt(PermissionContext context) { PermissionPromptHookTriggered = true; base.OnPermissionPrompt(context); } protected override void OnDeniedPermissionPrompt(PermissionContext context) { DeniedPermissionPromptHookTriggered = true; base.OnDeniedPermissionPrompt(context); } } private class CustomReturningAuthorizeFilter : FacebookAuthorizeFilter { private ActionResult _cannotCreateCookieResult; private ActionResult _promptPermissionHookResult; private ActionResult _deniedPermissionPromptHookResult; public CustomReturningAuthorizeFilter(FacebookConfiguration config, ActionResult cannotCreateCookieResult, ActionResult promptPermissionHookResult, ActionResult deniedPermissionPromptHookResult) : base(config) { _cannotCreateCookieResult = cannotCreateCookieResult; _promptPermissionHookResult = promptPermissionHookResult; _deniedPermissionPromptHookResult = deniedPermissionPromptHookResult; } protected override void OnCannotCreateCookies(PermissionContext context) { context.Result = _cannotCreateCookieResult; } protected override void OnPermissionPrompt(PermissionContext context) { context.Result = _promptPermissionHookResult; } protected override void OnDeniedPermissionPrompt(PermissionContext context) { context.Result = _deniedPermissionPromptHookResult; } } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookAuthorizeFilterTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Specialized; using System.Web; using System.Web.Mvc; using Facebook; using Microsoft.AspNet.Facebook.Authorization; using Microsoft.AspNet.Facebook.Client; using Microsoft.AspNet.Facebook.Providers; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.TestCommon; using Moq; namespace Microsoft.AspNet.Facebook.Test { public class FacebookAuthorizeFilterTest { [Fact] public void AddCookieVerificationQuery_AddsQueryParams() { // Arrange var collection = new NameValueCollection(); var filter = new CustomAuthorizeFilter(); // Act filter.ExposedAddCookieVerificationQuery(collection); // Assert Assert.NotEmpty(collection); } [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull( () => new FacebookAuthorizeFilter(null), "config"); } [Fact] public void OnAuthorization_ThrowsArgumentNullException() { FacebookConfiguration config = MockHelpers.CreateConfiguration(); FacebookAuthorizeFilter authorizeFilter = new FacebookAuthorizeFilter(config); Assert.ThrowsArgumentNull( () => authorizeFilter.OnAuthorization(null), "filterContext"); } [Fact] public void CreateRedirectResult_StringEncodesTheRedirectUrl() { Uri uri = new Uri("http://example.com?query=4'; alert('hello world')"); FacebookConfiguration config = MockHelpers.CreateConfiguration(); FacebookAuthorizeFilter authorizeFilter = new FacebookAuthorizeFilter(config); ContentResult result = Assert.IsType(authorizeFilter.CreateRedirectResult(uri)); Assert.Equal("text/html", result.ContentType); Assert.Equal(@"", result.Content); } [Fact] public void OnAuthorization_RedirectsToOAuthDialog_WhenSignedRequestIsNull() { FacebookConfiguration config = MockHelpers.CreateConfiguration(); FacebookAuthorizeFilter authorizeFilter = new FacebookAuthorizeFilter(config); AuthorizationContext context = new AuthorizationContext( MockHelpers.CreateControllerContext(), MockHelpers.CreateActionDescriptor(new[] { new FacebookAuthorizeAttribute("email") })); authorizeFilter.OnAuthorization(context); ContentResult result = Assert.IsType(context.Result); Assert.Equal("text/html", result.ContentType); Assert.Equal( "", result.Content); } [Fact] public void OnAuthorization_RedirectsToOAuthDialog_ForMissingPermissions() { FacebookClient client = MockHelpers.CreateFacebookClient(); IFacebookPermissionService permissionService = MockHelpers.CreatePermissionService(new[] { "" }); FacebookConfiguration config = MockHelpers.CreateConfiguration(client, permissionService); FacebookAuthorizeFilter authorizeFilter = new FacebookAuthorizeFilter(config); AuthorizationContext context = new AuthorizationContext( MockHelpers.CreateControllerContext(new NameValueCollection { {"signed_request", "exampleSignedRequest"} }), MockHelpers.CreateActionDescriptor(new[] { new FacebookAuthorizeAttribute("email", "user_likes") })); authorizeFilter.OnAuthorization(context); ContentResult result = Assert.IsType(context.Result); Assert.Equal("text/html", result.ContentType); Assert.Equal( "", result.Content); } [Theory] [InlineData("http://example.com", "https://www.facebook.com/dialog/oauth?redirect_uri=example.com")] [InlineData("http://example.com?error=access_denied", "https://apps.facebook.com/DefaultAppId/home/permissions?originUrl=https%3a%2f%2fapps.facebook.com%2fDefaultAppId%2f\\u0026permissions=email")] public void OnAuthorization_RedirectsToAuthorizationRedirectPath_OnlyWhenUserDeniedGrantingPermissions(string requestUrl, string expectedRedirectUrl) { FacebookClient client = MockHelpers.CreateFacebookClient(); IFacebookPermissionService permissionService = MockHelpers.CreatePermissionService(new[] { "" }); FacebookConfiguration config = MockHelpers.CreateConfiguration(client, permissionService); config.AuthorizationRedirectPath = "~/home/permissions"; FacebookAuthorizeFilter authorizeFilter = new FacebookAuthorizeFilter(config); AuthorizationContext context = new AuthorizationContext( MockHelpers.CreateControllerContext(new NameValueCollection { {"signed_request", "exampleSignedRequest"} }, null, new Uri(requestUrl)), MockHelpers.CreateActionDescriptor(new[] { new FacebookAuthorizeAttribute("email") })); authorizeFilter.OnAuthorization(context); ContentResult result = Assert.IsAssignableFrom(context.Result); Assert.Equal("text/html", result.ContentType); Assert.Equal( String.Format("", expectedRedirectUrl), result.Content); } private class CustomAuthorizeFilter : FacebookAuthorizeFilter { public CustomAuthorizeFilter() : base(new FacebookConfiguration()) { } public void ExposedAddCookieVerificationQuery(NameValueCollection queries) { AddCookieVerificationQuery(queries); } } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookClientExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; using Microsoft.AspNet.Facebook.Client; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.AspNet.Facebook.Test.Types; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookClientExtensionsTest { [Fact] public async Task GetCurrentUserAsyncOfT_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserAsync(); Assert.Equal("me?fields=id,name,picture.fields(url)", client.Path); } [Fact] public async Task GetCurrentUserAsync_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserAsync(); Assert.Equal("me", client.Path); } [Fact] public async Task GetCurrentUserFriendsAsyncOfT_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserFriendsAsync(); Assert.Equal("me/friends?fields=id,name,picture.fields(url)", client.Path); } [Fact] public async Task GetCurrentUserFriendsAsync_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserFriendsAsync(); Assert.Equal("me/friends", client.Path); } [Fact] public async Task GetCurrentUserPermissionsAsync_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserPermissionsAsync(); Assert.Equal("me/permissions", client.Path); } [Fact] public async Task GetCurrentUserPhotosAsyncOfT_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserPhotosAsync(); Assert.Equal("me/photos?fields=name,picture,source", client.Path); } [Fact] public async Task GetCurrentUserStatusesAsyncOfT_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetCurrentUserStatusesAsync(); Assert.Equal("me/statuses?fields=message,time", client.Path); } [Fact] public async Task GetFacebookObjectAsyncOfT_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetFacebookObjectAsync>("me/picture"); Assert.Equal("me/picture?fields=url", client.Path); } [Fact] public async Task GetFacebookObjectAsync_CallsGetTaskAsyncWithTheExpectedPath() { LocalFacebookClient client = new LocalFacebookClient(); await client.GetFacebookObjectAsync("me/notes"); Assert.Equal("me/notes", client.Path); } [Fact] public async Task GetFacebookObjectAsyncOfT_ThrowArgumentNullExceptions() { LocalFacebookClient client = null; await Assert.ThrowsArgumentNullAsync(() => client.GetFacebookObjectAsync("me"), "client"); client = new LocalFacebookClient(); await Assert.ThrowsArgumentNullAsync(() => client.GetFacebookObjectAsync(null), "objectPath"); } [Fact] public async Task GetFacebookObjectAsync_ThrowArgumentNullExceptions() { LocalFacebookClient client = null; await Assert.ThrowsArgumentNullAsync(() => client.GetFacebookObjectAsync("me"), "client"); client = new LocalFacebookClient(); await Assert.ThrowsArgumentNullAsync(() => client.GetFacebookObjectAsync(null), "objectPath"); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookConfigurationTest { [Fact] public void Default_Constructor() { FacebookConfiguration config = new FacebookConfiguration(); Assert.Null(config.AppId); Assert.Null(config.AppNamespace); Assert.Null(config.AppSecret); Assert.NotNull(config.AppUrl); Assert.Null(config.AuthorizationRedirectPath); Assert.Null(config.CannotCreateCookieRedirectPath); Assert.Null(config.ClientProvider); Assert.Null(config.PermissionService); Assert.NotNull(config.Properties); } [Fact] public void AppUrl_FromAppId() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "654321"; Assert.Equal("https://apps.facebook.com/654321", config.AppUrl); } [Fact] public void AppUrl_FromAppNamespace() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "654321"; config.AppNamespace = "MyCustomApp"; Assert.Equal("https://apps.facebook.com/MyCustomApp", config.AppUrl); } [Fact] public void AppUrl_FromCustomUrl() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "654321"; config.AppNamespace = "MyCustomApp"; config.AppUrl = "http://apps.example.com/myapp"; Assert.Equal("http://apps.example.com/myapp", config.AppUrl); } [Fact] public void LoadFromAppSettings_ReadsFromAppConfig() { FacebookConfiguration config = new FacebookConfiguration(); config.LoadFromAppSettings(); Assert.Equal("123456", config.AppId); Assert.Equal("abcdefg", config.AppSecret); Assert.Equal("MyApp", config.AppNamespace); Assert.Equal("~/Authorize/Index", config.AuthorizationRedirectPath); Assert.Equal("~/NoCookies/Index", config.CannotCreateCookieRedirectPath); Assert.Equal("https://apps.newfacebook.example.com/myapp", config.AppUrl); } [Fact] public void AuthorizationRedirectPath_ThrowsArgumentException() { FacebookConfiguration config = new FacebookConfiguration(); Assert.ThrowsArgument(() => config.AuthorizationRedirectPath = "Home/Permissions", "value"); } [Fact] public void CannotCreateCookieRedirectPath_ThrowsArgumentException() { FacebookConfiguration config = new FacebookConfiguration(); Assert.ThrowsArgument(() => config.CannotCreateCookieRedirectPath = "Home/Permissions", "value"); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookContextModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Web.Mvc; using Microsoft.AspNet.Facebook.ModelBinders; using Microsoft.AspNet.Facebook.Providers; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookContextModelBinderTest { [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull(() => new FacebookContextModelBinder(null), "config"); } [Fact] public void BindModel_ReturnsExpectedFacebookContext_WhenSignedRequestComesFromForm() { FacebookConfiguration config = new FacebookConfiguration(); config.AppSecret = "3e29b24f825e737d97aed5eb62df5076"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookContextModelBinder contextBinder = new FacebookContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext(new NameValueCollection { {"signed_request", "x1yDEgacN3N5iu23Ji8NLYp9LGO1-cUXKHTJQrMqzVQ.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzNTM5MTMyMDAsImlzc3VlZF9hdCI6MTM1MzkwNzQ5Miwib2F1dGhfdG9rZW4iOiJBQUFGUlJPcWtwZ01CQURBSjNQZk5vNldXMlJ5WkFSQ1hjU0daQlhpNTBLTG9wRzFwYmFwc2M2aThKY3h6WkFQN1pDSnlpcXVHYlc3WXlCam1aQjh0UWpyelZ2VTNrYm44b3N3WXR5czkzTWdaRFpEIiwidXNlciI6eyJjb3VudHJ5IjoidXMiLCJsb2NhbGUiOiJlbl9VUyIsImFnZSI6eyJtaW4iOjIxfX0sInVzZXJfaWQiOiIxNzgyNTkwMSJ9"} }); ModelBindingContext modelBindingContext = new ModelBindingContext(); FacebookContext context = Assert.IsType(contextBinder.BindModel(controllerContext, modelBindingContext)); Assert.NotNull((object)context.SignedRequest); Assert.NotNull(context.AccessToken); Assert.Equal("17825901", context.UserId); } [Fact] public void BindModel_ReturnsExpectedFacebookContext_WhenSignedRequestComesFromQuery() { FacebookConfiguration config = new FacebookConfiguration(); config.AppSecret = "3e29b24f825e737d97aed5eb62df5076"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookContextModelBinder contextBinder = new FacebookContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext( null, new NameValueCollection { {"signed_request", "x1yDEgacN3N5iu23Ji8NLYp9LGO1-cUXKHTJQrMqzVQ.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzNTM5MTMyMDAsImlzc3VlZF9hdCI6MTM1MzkwNzQ5Miwib2F1dGhfdG9rZW4iOiJBQUFGUlJPcWtwZ01CQURBSjNQZk5vNldXMlJ5WkFSQ1hjU0daQlhpNTBLTG9wRzFwYmFwc2M2aThKY3h6WkFQN1pDSnlpcXVHYlc3WXlCam1aQjh0UWpyelZ2VTNrYm44b3N3WXR5czkzTWdaRFpEIiwidXNlciI6eyJjb3VudHJ5IjoidXMiLCJsb2NhbGUiOiJlbl9VUyIsImFnZSI6eyJtaW4iOjIxfX0sInVzZXJfaWQiOiIxNzgyNTkwMSJ9"} }); FacebookContext context = Assert.IsType(contextBinder.BindModel(controllerContext, new ModelBindingContext())); Assert.NotNull((object)context.SignedRequest); Assert.NotNull(context.AccessToken); Assert.Equal("17825901", context.UserId); } [Fact] public void BindModel_ReturnsInvalidModelState_WhenSignedRequestIsNull() { FacebookConfiguration config = new FacebookConfiguration(); config.AppSecret = "abcdef"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookContextModelBinder contextBinder = new FacebookContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext(); ModelBindingContext modelBindingContext = new ModelBindingContext(); object context = contextBinder.BindModel(controllerContext, modelBindingContext); Assert.Null(context); Assert.False(modelBindingContext.ModelState.IsValid); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookQueryHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNet.Facebook.Client; using Microsoft.AspNet.Facebook.Test.Types; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookQueryHelperTest { [Theory] [InlineData(typeof(SimpleUser), "?fields=id,name,picture.fields(url)")] [InlineData(typeof(UserWithFriends), "?fields=id,name,picture.fields(url),friends.fields(id,name,picture.fields(url))")] [InlineData(typeof(UserTypeWithIgnoredProperties), "?fields=id")] [InlineData(typeof(UserTypeWithRenamedProperties), "?fields=id,name,picture.fields(url)")] [InlineData(typeof(UserTypeWithFieldModifiers), "?fields=id,name,picture.type(large).fields(url),friends.limit(5).fields(id,name,picture.fields(url))")] public void GetFields_ReturnsExpectedQuery(Type modelType, string expectedQuery) { Assert.Equal(expectedQuery, FacebookQueryHelper.GetFields(modelType)); } [Theory] [InlineData(typeof(UserWithUserFriends))] [InlineData(typeof(UserContainingFriendsWithCycle))] public void GetFields_ThrowsExceptionWhenDetectsACycle(Type modelType) { Assert.Throws( () => FacebookQueryHelper.GetFields(modelType), Resources.CircularReferenceNotSupported); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookRealtimeControllerTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Facebook.Models; using Microsoft.AspNet.Facebook.Providers; using Microsoft.AspNet.Facebook.Realtime; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookRealtimeControllerTest { private const string AppSignatureHeader1 = "sha1=dbb5c0bfaac69ffee7b633e88d85e107fba7ecca"; private const string AppSecret1 = "f8ad79c0081a80bb885e2b280c3f8442"; private const string AppSignatureHeader2 = "sha1=3ee3a233ca0c872cae6b40d38a99dff26bf8eb27"; private const string AppSecret2 = "134cfa3691d1f51c64e70700f397ed20"; private const string ContentString = "{\"object\":\"user\",\"entry\":[{\"uid\":\"17825901\",\"id\":\"17825901\",\"time\":1352251746,\"changed_fields\":[\"likes\"]}]}"; [Fact] public void Overriding_VerificationToken() { var userRealTimeController = new UserRealtimeCallbackController(); Assert.Null(userRealTimeController.VerifyToken); userRealTimeController = new UserRealtimeCallbackController(null, "foo"); Assert.Equal("foo", userRealTimeController.VerifyToken); userRealTimeController = new UserRealtimeCallbackController(null, String.Empty); Assert.Equal(String.Empty, userRealTimeController.VerifyToken); } [Theory] [InlineData("123456", "foo")] [InlineData("654321", "bar")] public async Task Get_ReturnsOk_WithValidParameters(string challenge, string verifyToken) { var userRealTimeController = new UserRealtimeCallbackController(null, verifyToken); userRealTimeController.Request = new HttpRequestMessage(); var subscriptionVerification = new SubscriptionVerification { Challenge = challenge, Mode = "subscribe", Verify_Token = verifyToken }; Assert.Equal(challenge, await userRealTimeController.Get(subscriptionVerification).Content.ReadAsStringAsync()); } [Theory] [InlineData(null, "foo", HttpStatusCode.BadRequest)] [InlineData("654321", null, HttpStatusCode.BadRequest)] [InlineData("654321", "", HttpStatusCode.BadRequest)] [InlineData("", "bar", HttpStatusCode.BadRequest)] [InlineData("654321", "bar", HttpStatusCode.OK)] public void Get_ReturnsExpectedStatusCode(string challenge, string verifyToken, HttpStatusCode expectedStatusCode) { var userRealTimeController = new UserRealtimeCallbackController(null, verifyToken); userRealTimeController.Request = new HttpRequestMessage(); var subscriptionVerification = new SubscriptionVerification { Challenge = challenge, Mode = "subscribe", Verify_Token = "bar" }; Assert.Equal(expectedStatusCode, userRealTimeController.Get(subscriptionVerification).StatusCode); } [Theory] [InlineData(ContentString, AppSignatureHeader1, AppSecret1)] [InlineData(ContentString, AppSignatureHeader2, AppSecret2)] public async Task Post_ReturnsOk_WithValidParameters(string contentString, string headerValue, string appSecret) { var userRealTimeController = new UserRealtimeCallbackController(appSecret, null); userRealTimeController.Request = new HttpRequestMessage { Content = new StringContent(contentString, Encoding.UTF8, "text/json") }; var request = userRealTimeController.Request; request.Headers.Add("X-Hub-Signature", headerValue); Assert.Equal(HttpStatusCode.OK, (await userRealTimeController.Post()).StatusCode); } [Theory] [InlineData(ContentString, AppSignatureHeader1, AppSecret2)] [InlineData(ContentString, AppSignatureHeader2, AppSecret1)] [InlineData(ContentString, null, AppSecret2)] [InlineData(ContentString, AppSignatureHeader1, null)] public async Task Post_ReturnsBadRequest_WithInValidParameters(string contentString, string headerValue, string AppSecret) { var userRealTimeController = new UserRealtimeCallbackController(AppSecret, null); userRealTimeController.Request = new HttpRequestMessage { Content = new StringContent(contentString, Encoding.UTF8, "text/json") }; var request = userRealTimeController.Request; if (headerValue != null) { request.Headers.Add("X-Hub-Signature", headerValue); } Assert.Equal(HttpStatusCode.BadRequest, (await userRealTimeController.Post()).StatusCode); } private sealed class UserRealtimeCallbackController : FacebookRealtimeUpdateController { private string _verifyToken; public UserRealtimeCallbackController() : this(null, null) { } public UserRealtimeCallbackController(string appSecret, string verifyToken) { // Avoid base class's fallback to static GlobalFacebookConfiguration. var config = new FacebookConfiguration(); config.ClientProvider = new DefaultFacebookClientProvider(config); config.PermissionService = new DefaultFacebookPermissionService(config); config.AppSecret = appSecret; FacebookConfiguration = config; _verifyToken = verifyToken; } public override string VerifyToken { get { return _verifyToken; } } public override Task HandleUpdateAsync(ChangeNotification notification) { return Task.FromResult(0); } } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/FacebookRedirectContextModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Web.Mvc; using Microsoft.AspNet.Facebook.ModelBinders; using Microsoft.AspNet.Facebook.Providers; using Microsoft.AspNet.Facebook.Test.Helpers; using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class FacebookRedirectContextModelBinderTest { [Fact] public void Constructor_ThrowsArgumentNullException() { Assert.ThrowsArgumentNull(() => new FacebookContextModelBinder(null), "config"); } [Fact] public void BindModel_ReturnsExpectedFacebookRedirectContext() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "123456"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookRedirectContextModelBinder redirectContextBinder = new FacebookRedirectContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext( null, new NameValueCollection { {"originUrl", "https://apps.facebook.com/123456/home/index"}, {"permissions", "email,user_likes"} }); ModelBindingContext modelBindingContext = new ModelBindingContext(); FacebookRedirectContext context = Assert.IsType(redirectContextBinder.BindModel(controllerContext, modelBindingContext)); Assert.Equal("https://apps.facebook.com/123456/home/index", context.OriginUrl); // Redirect URL should not have any permissions on it. That's handled by the authorization filter. Assert.Equal("https://www.facebook.com/dialog/oauth?redirect_uri=https%3A%2F%2Fapps.facebook.com%2F123456%2Fhome%2Findex&client_id=123456", context.RedirectUrl); Assert.Equal(2, context.RequiredPermissions.Length); Assert.Equal("email", context.RequiredPermissions[0]); Assert.Equal("user_likes", context.RequiredPermissions[1]); Assert.Same(config, context.Configuration); } [Fact] public void BindModel_ReturnsInvalidModelState_WhenOriginUrlIsNull() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "123456"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookRedirectContextModelBinder redirectContextBinder = new FacebookRedirectContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext( null, new NameValueCollection { {"permissions", "email,user_likes"} }); ModelBindingContext modelBindingContext = new ModelBindingContext(); FacebookRedirectContext context = Assert.IsType(redirectContextBinder.BindModel(controllerContext, modelBindingContext)); Assert.False(modelBindingContext.ModelState.IsValid); } [Fact] public void BindModel_ReturnsInvalidModelState_WhenOriginUrlIsExternal() { FacebookConfiguration config = new FacebookConfiguration(); config.AppId = "123456"; config.ClientProvider = new DefaultFacebookClientProvider(config); FacebookRedirectContextModelBinder redirectContextBinder = new FacebookRedirectContextModelBinder(config); ControllerContext controllerContext = MockHelpers.CreateControllerContext( null, new NameValueCollection { {"originUrl", "https://example.com/123456/home/index"}, {"permissions", "email,user_likes"} }); ModelBindingContext modelBindingContext = new ModelBindingContext(); FacebookRedirectContext context = Assert.IsType(redirectContextBinder.BindModel(controllerContext, modelBindingContext)); Assert.False(modelBindingContext.ModelState.IsValid); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/GlobalFacebookConfigurationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace Microsoft.AspNet.Facebook.Test { public class GlobalFacebookConfigurationTest { [Fact] public void Default_Configuration() { FacebookConfiguration config = GlobalFacebookConfiguration.Configuration; Assert.Null(config.AppId); Assert.Null(config.AppNamespace); Assert.Null(config.AppSecret); Assert.NotNull(config.AppUrl); Assert.Null(config.AuthorizationRedirectPath); Assert.NotNull(config.ClientProvider); Assert.NotNull(config.PermissionService); Assert.NotNull(config.Properties); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Helpers/LocalFacebookClient.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading.Tasks; using Facebook; namespace Microsoft.AspNet.Facebook.Test.Helpers { public class LocalFacebookClient : FacebookClient { public string Path { get; set; } public override Task GetTaskAsync(string path) { Path = path; return Task.FromResult(Activator.CreateInstance()); } public override Task GetTaskAsync(string path) { Path = path; return Task.FromResult(new object()); } public override object Get(string path) { Path = path; return new object(); } public override TResult Get(string path) { Path = path; return Activator.CreateInstance(); } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Helpers/MockHelpers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Dynamic; using System.Web; using System.Web.Mvc; using Facebook; using Microsoft.AspNet.Facebook.Providers; using Moq; namespace Microsoft.AspNet.Facebook.Test.Helpers { internal class MockHelpers { public static ControllerContext CreateControllerContext(NameValueCollection requestFormData = null, NameValueCollection requestQueryData = null, Uri requestUrl = null, HttpCookieCollection requestCookies = null) { Mock controllerContext = new Mock(); controllerContext.Setup(c => c.HttpContext.Response).Returns(new EmptyHttpResponse()); controllerContext.Setup(c => c.HttpContext.Response.Cookies).Returns(new HttpCookieCollection()); controllerContext.Setup(c => c.HttpContext.Items).Returns(new Dictionary()); controllerContext.Setup(c => c.HttpContext.Request.Url).Returns(requestUrl ?? new Uri("http://example.com")); controllerContext.Setup(c => c.HttpContext.Request.AppRelativeCurrentExecutionFilePath).Returns("~/"); controllerContext.Setup(c => c.HttpContext.Request.Form).Returns(requestFormData ?? new NameValueCollection()); controllerContext.Setup(c => c.HttpContext.Request.QueryString).Returns(requestQueryData ?? new NameValueCollection()); controllerContext.Setup(c => c.HttpContext.Request.Cookies).Returns(requestCookies ?? new HttpCookieCollection()); return controllerContext.Object; } public static ActionDescriptor CreateActionDescriptor(object[] actionAuthorizeAttributes = null, object[] controllerAuthorizeAttributes = null) { Mock actionDescriptor = new Mock(); actionDescriptor.Setup(a => a.GetCustomAttributes(typeof(FacebookAuthorizeAttribute), true)).Returns(actionAuthorizeAttributes ?? new object[0]); actionDescriptor.Setup(a => a.ControllerDescriptor.GetCustomAttributes(typeof(FacebookAuthorizeAttribute), true)).Returns(controllerAuthorizeAttributes ?? new object[0]); return actionDescriptor.Object; } public static FacebookConfiguration CreateConfiguration(FacebookClient client = null, IFacebookPermissionService permissionService = null) { FacebookConfiguration config = new FacebookConfiguration(); if (client == null) { config.ClientProvider = new DefaultFacebookClientProvider(config); config.AppId = "DefaultAppId"; config.AppSecret = "DefaultAppSecret"; } else { Mock clientProvider = new Mock(); clientProvider.Setup(cp => cp.CreateClient()).Returns(client); config.ClientProvider = clientProvider.Object; config.AppId = client.AppId ?? "DefaultAppId"; config.AppSecret = client.AppSecret ?? "DefaultAppSecret"; } config.PermissionService = permissionService ?? new DefaultFacebookPermissionService(config); return config; } public static FacebookClient CreateFacebookClient() { Mock client = new Mock(); dynamic signedRequestParameters = new ExpandoObject(); signedRequestParameters.user_id = "sampleId"; signedRequestParameters.oauth_token = "sampleToken"; client.Setup(c => c.ParseSignedRequest(It.IsAny())).Returns((object)signedRequestParameters); client.Setup(c => c.GetLoginUrl(It.IsAny())).Returns(new Uri("https://www.facebook.com/dialog/oauth?redirect_uri=example.com")); return client.Object; } public static IFacebookPermissionService CreatePermissionService(string[] permissionsToReturn, PermissionsStatus permissionsStatusToReturn = null) { var client = new Mock(); permissionsStatusToReturn = permissionsStatusToReturn ?? new PermissionsStatus(apiResult: null); client.Setup(p => p.GetUserPermissions(It.IsAny(), It.IsAny())).Returns(permissionsToReturn); client.Setup(p => p.GetUserPermissionsStatus(It.IsAny(), It.IsAny())).Returns(permissionsStatusToReturn); return client.Object; } private sealed class EmptyHttpResponse : HttpResponseBase { } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Microsoft.AspNet.Facebook.Test.csproj ================================================  {C3BEF382-C7C4-454D-B017-1EAC03E9A82C} Library Properties Microsoft.AspNet.Facebook.Test Microsoft.AspNet.Facebook.Test ..\..\bin\$(Configuration)\Test\ ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll True ..\..\packages\Facebook.6.4.2\lib\net45\Facebook.dll ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll True ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True Designer {821a136c-7c6f-44c6-a9e6-c39b5bfb1483} Microsoft.AspNet.Facebook {668e9021-ce84-49d9-98fb-df125a9fcdb0} System.Net.Http.Formatting {9b7e3740-6161-4548-833c-4bbca43b970e} System.Web.Helpers {ddc1ce0c-486e-4e35-bb3b-eab61f8f9440} System.Web.Http {3d3ffd8a-624d-4e9b-954b-e1c105507975} System.Web.Mvc {8f18041b-9410-4c36-a9c5-067813df5f31} System.Web.Razor {22babb60-8f02-4027-affc-acf069954536} System.Web.WebPages.Deployment {0939b11a-fe4e-4ba1-8ad6-d97741ee314f} System.Web.WebPages.Razor {76efa9c5-8d7e-4fdf-b710-e20f8b6b00d2} System.Web.WebPages {fccc4cb7-baf7-4a57-9f89-e5766fe536c0} Microsoft.TestCommon This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/FacebookPicture.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class FacebookPicture { public string Url { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/SimpleUser.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class SimpleUser { public string Id { get; set; } public string Name { get; set; } public FacebookConnection Picture { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserPhoto.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class UserPhoto { public string Name { get; set; } public string Picture { get; set; } public string Source { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserStatus.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class UserStatus { public string Message { get; set; } public long Time { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserTypeWithFieldModifiers.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class UserTypeWithFieldModifiers { public string Id { get; set; } public string Name { get; set; } [FacebookFieldModifier("type(large)")] public FacebookConnection Picture { get; set; } [FacebookFieldModifier("limit(5)")] public FacebookGroupConnection Friends { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserTypeWithIgnoredProperties.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Newtonsoft.Json; namespace Microsoft.AspNet.Facebook.Test.Types { public class UserTypeWithIgnoredProperties { public string Id { get; set; } [JsonIgnore] public string Name { get; set; } [JsonIgnore] public FacebookConnection Picture { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserTypeWithRenamedProperties.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Newtonsoft.Json; namespace Microsoft.AspNet.Facebook.Test.Types { public class UserTypeWithRenamedProperties { [JsonProperty("Id")] public string FacebookId { get; set; } public string Name { get; set; } [JsonProperty("picture")] public FacebookConnection PictureConnection { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserTypesWithCycles.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class UserWithUserFriends { public string Id { get; set; } public string Name { get; set; } public FacebookGroupConnection Friends { get; set; } } public class UserContainingFriendsWithCycle { public string Id { get; set; } public FacebookGroupConnection Friends { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/Types/UserWithFriends.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.AspNet.Facebook.Test.Types { public class UserWithFriends { public string Id { get; set; } public string Name { get; set; } public FacebookConnection Picture { get; set; } public FacebookGroupConnection Friends { get; set; } } } ================================================ FILE: test/Microsoft.AspNet.Facebook.Test/packages.config ================================================  ================================================ FILE: test/Microsoft.TestCommon/AppDomainUtils.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Reflection; using System.Web.Compilation; using System.Web.Hosting; namespace System.Web.WebPages.TestUtils { public static class AppDomainUtils { // Allow a test to modify static fields in an independent appdomain so that // other tests will not be affected. public static void RunInSeparateAppDomain(Action action) { RunInSeparateAppDomain(new AppDomainSetup(), action); } public static void RunInSeparateAppDomain(AppDomainSetup setup, Action action) { var dir = Path.GetDirectoryName(typeof(AppDomainUtils).Assembly.CodeBase).Replace("file:\\", ""); setup.PrivateBinPath = dir; setup.ApplicationBase = dir; setup.ApplicationName = Guid.NewGuid().ToString(); setup.ShadowCopyFiles = "true"; setup.ShadowCopyDirectories = setup.ApplicationBase; setup.CachePath = Path.Combine(Path.GetTempPath(), setup.ApplicationName); AppDomain appDomain = null; try { appDomain = AppDomain.CreateDomain(setup.ApplicationName, null, setup); AppDomainHelper helper = appDomain.CreateInstanceAndUnwrap(typeof(AppDomainUtils).Assembly.FullName, typeof(AppDomainHelper).FullName) as AppDomainHelper; helper.Run(action); } finally { if (appDomain != null) { AppDomain.Unload(appDomain); } } } public class AppDomainHelper : MarshalByRefObject { public void Run(Action action) { action(); } } public static void SetPreAppStartStage() { var stage = typeof(BuildManager).GetProperty("PreStartInitStage", BindingFlags.Static | BindingFlags.NonPublic); var value = ((FieldInfo)typeof(BuildManager).Assembly.GetType("System.Web.Compilation.PreStartInitStage").GetMember("DuringPreStartInit")[0]).GetValue(null); stage.SetValue(null, value, new object[] { }); SetAppData(); var env = new HostingEnvironment(); } public static void SetAppData() { var appdomain = AppDomain.CurrentDomain; // Set some dummy values to make the appdomain seem more like a ASP.NET hosted one appdomain.SetData(".appDomain", "*"); appdomain.SetData(".appId", "appId"); appdomain.SetData(".appPath", "appPath"); appdomain.SetData(".appVPath", "/WebSite1"); appdomain.SetData(".domainId", "1"); } } } ================================================ FILE: test/Microsoft.TestCommon/Assert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon { // This extends xUnit.net's Assert class, and makes it partial so that we can // organize the extension points by logical functionality (rather than dumping them // all into this single file). // // See files named XxxAssertions for root extensions to Assert. public partial class Assert : Xunit.Assert { public static readonly ReflectionAssert Reflection = new ReflectionAssert(); public static readonly TypeAssert Type = new TypeAssert(); public static readonly HttpAssert Http = new HttpAssert(); public static readonly MediaTypeAssert MediaType = new MediaTypeAssert(); public static readonly GenericTypeAssert GenericType = new GenericTypeAssert(); public static readonly SerializerAssert Serializer = new SerializerAssert(); public static readonly StreamAssert Stream = new StreamAssert(); public static readonly TaskAssert Task = new TaskAssert(); public static readonly XmlAssert Xml = new XmlAssert(); // Method has been marked [Obsolete] in xUnit.net v2.0.0+. public static new void ReferenceEquals(object a, object b) { Same(a, b); } } } ================================================ FILE: test/Microsoft.TestCommon/CultureReplacer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Threading; namespace Microsoft.TestCommon { public class CultureReplacer : IDisposable { private const string _defaultCultureName = "en-GB"; private const string _defaultUICultureName = "en-US"; private static readonly CultureInfo _defaultCulture = CultureInfo.GetCultureInfo(_defaultCultureName); private readonly CultureInfo _originalCulture; private readonly CultureInfo _originalUICulture; private readonly long _threadId; // Culture => Formatting of dates/times/money/etc, defaults to en-GB because en-US is the same as InvariantCulture // We want to be able to find issues where the InvariantCulture is used, but a specific culture should be. // // UICulture => Language public CultureReplacer(string culture = _defaultCultureName, string uiCulture = _defaultUICultureName) { _originalCulture = Thread.CurrentThread.CurrentCulture; _originalUICulture = Thread.CurrentThread.CurrentUICulture; _threadId = Thread.CurrentThread.ManagedThreadId; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(uiCulture); } /// /// The name of the culture that is used as the default value for Thread.CurrentCulture when CultureReplacer is used. /// public static string DefaultCultureName { get { return _defaultCultureName; } } /// /// The name of the culture that is used as the default value for Thread.UICurrentCulture when CultureReplacer is used. /// public static string DefaultUICultureName { get { return _defaultUICultureName; } } /// /// The culture that is used as the default value for Thread.CurrentCulture when CultureReplacer is used. /// public static CultureInfo DefaultCulture { get { return _defaultCulture; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) { Assert.True(Thread.CurrentThread.ManagedThreadId == _threadId, "The current thread is not the same as the thread invoking the constructor. This should never happen."); Thread.CurrentThread.CurrentCulture = _originalCulture; Thread.CurrentThread.CurrentUICulture = _originalUICulture; } } } } ================================================ FILE: test/Microsoft.TestCommon/DataAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Reflection; namespace Microsoft.TestCommon { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public abstract class DataAttribute : Xunit.Sdk.DataAttribute { public abstract IEnumerable GetData(MethodInfo methodUnderTest, Type[] parameterTypes); public override IEnumerable GetData(MethodInfo methodUnderTest) { return GetData(methodUnderTest, parameterTypes: null); } } } ================================================ FILE: test/Microsoft.TestCommon/DictionaryEqualityComparer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; namespace Microsoft.TestCommon { public class DictionaryEqualityComparer : IEqualityComparer> { public bool Equals(IDictionary x, IDictionary y) { if (x.Count != y.Count) { return false; } foreach (string key in x.Keys) { object xVal = x[key]; object yVal; if (!y.TryGetValue(key, out yVal)) { return false; } if (xVal == null) { if (yVal == null) { continue; } return false; } if (!xVal.Equals(yVal)) { return false; } } return true; } public int GetHashCode(IDictionary obj) { return 1; } } } ================================================ FILE: test/Microsoft.TestCommon/Directory.Build.props ================================================ obj\ns1_3\ $(DefaultItemExcludes);obj\** ================================================ FILE: test/Microsoft.TestCommon/EnumHelperTestBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon { public abstract class EnumHelperTestBase where TEnum : IComparable, IFormattable, IConvertible { private Func _isDefined; private Action _validate; private TEnum _undefined; /// /// Helper to verify that we validate enums correctly when passed as arguments etc. /// /// A Func used to validate that a value is defined. /// A Func used to validate that a value is definded of throw an exception. /// An undefined value. protected EnumHelperTestBase(Func isDefined, Action validate, TEnum undefined) { _isDefined = isDefined; _validate = validate; _undefined = undefined; } [Fact] public void IsDefined_ReturnsTrueForDefinedValues() { Array values = Enum.GetValues(typeof(TEnum)); foreach (object value in values) { if (ValueExistsForFramework((TEnum)value)) { Assert.True(_isDefined((TEnum)value)); } else { Assert.False(_isDefined((TEnum)value)); } } } [Fact] public void IsDefined_ReturnsFalseForUndefinedValues() { Assert.False(_isDefined(_undefined)); } [Fact] public void Validate_DoesNotThrowForDefinedValues() { Array values = Enum.GetValues(typeof(TEnum)); foreach (object value in values) { if (ValueExistsForFramework((TEnum)value)) { _validate((TEnum)value, "parameter"); } } } [Fact] public void Validate_ThrowsForUndefinedValues() { AssertForUndefinedValue( () => _validate(_undefined, "parameter"), "parameter", (int)Convert.ChangeType(_undefined, typeof(int)), typeof(TEnum), allowDerivedExceptions: false); } /// /// Override this if InvalidEnumArgument is not supported in the targetted platform /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The expected invalid value that should appear in the message /// The type of the enumeration /// Pass true to allow exceptions which derive from TException; pass false, otherwise protected virtual void AssertForUndefinedValue(Action testCode, string parameterName, int invalidValue, Type enumType, bool allowDerivedExceptions = false) { Assert.ThrowsInvalidEnumArgument( testCode, parameterName, invalidValue, enumType, allowDerivedExceptions); } /// /// Override this to determine if a given enum value for an enum exists in a given framework /// /// /// Wheter the value exists protected virtual bool ValueExistsForFramework(TEnum value) { return true; } } } ================================================ FILE: test/Microsoft.TestCommon/ExceptionAssertions.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.Reflection; using System.Threading.Tasks; using System.Web; #if Testing_NetStandard1_3 using System.Web.Http; #endif namespace Microsoft.TestCommon { public partial class Assert { // Method has been removed in xUnit.net v2.0.0+. public static void DoesNotThrow(Action testCode) { testCode(); } /// /// Verifies that the exact exception is thrown (and not a derived exception type). /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static new T Throws(Action testCode) where T : Exception { return (T)Throws(typeof(T), testCode); } /// /// Verifies that the exact exception is thrown (and not a derived exception type). /// Generally used to test property accessors. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static new T Throws(Func testCode) where T : Exception { return (T)Throws(typeof(T), testCode); } /// /// Verifies that the exact exception is thrown (and not a derived exception type). /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static new Exception Throws(Type exceptionType, Action testCode) { Exception exception = RecordException(testCode); return VerifyException(exceptionType, exception); } /// /// Verifies that the exact exception is thrown (and not a derived exception type). /// Generally used to test property accessors. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static new Exception Throws(Type exceptionType, Func testCode) { return Throws(exceptionType, () => { testCode(); }); } /// /// Verifies that an exception of the given type (or optionally a derived type) is thrown. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static TException Throws(Action testCode, bool allowDerivedExceptions) where TException : Exception { Type exceptionType = typeof(TException); Exception exception = RecordException(testCode); TargetInvocationException tie = exception as TargetInvocationException; if (tie != null) { exception = tie.InnerException; } if (exception == null) { throw new ThrowsException(exceptionType); } var typedException = exception as TException; if (typedException == null || (!allowDerivedExceptions && typedException.GetType() != typeof(TException))) { throw new ThrowsException(exceptionType, exception); } return typedException; } /// /// Verifies that an exception of the given type (or optionally a derived type) is thrown. /// Generally used to test property accessors. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static TException Throws(Func testCode, bool allowDerivedExceptions) where TException : Exception { return Throws(() => { testCode(); }, allowDerivedExceptions); } /// /// Verifies that an exception of the given type (or optionally a derived type) is thrown. /// Also verifies that the exception message matches. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception message to verify /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static TException Throws(Action testCode, string exceptionMessage, bool allowDerivedExceptions = false) where TException : Exception { var ex = Throws(testCode, allowDerivedExceptions); VerifyExceptionMessage(ex, exceptionMessage); return ex; } /// /// Verifies that an exception of the given type (or optionally a derived type) is thrown. /// Also verified that the exception message matches. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception message to verify /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static TException Throws(Func testCode, string exceptionMessage, bool allowDerivedExceptions = false) where TException : Exception { return Throws(() => { testCode(); }, exceptionMessage, allowDerivedExceptions); } /// /// Verifies that the code throws an (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsArgument(Action testCode, string paramName, bool allowDerivedExceptions = false) { var ex = Throws(testCode, allowDerivedExceptions); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies the given throws an . /// /// A delegate to the code to be tested. /// The name of the parameter that should throw the exception. /// A that on completion returns the exception that was thrown. public static async Task ThrowsArgumentAsync(Func testCode, string paramName) { var ex = await ThrowsAsync(testCode); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the code throws an (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The exception message to verify /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsArgument(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false) { var ex = Throws(testCode, allowDerivedExceptions); if (paramName != null) { Equal(paramName, ex.ParamName); } VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true); return ex; } /// /// Verifies that the throws an . /// /// A delegate to the code to be tested. /// The name of the parameter that should throw the exception. /// The exception message to verify (or a portion of the expected message). /// A that on completion returns the exception that was thrown. public static async Task ThrowsArgumentAsync(Func testCode, string paramName, string exceptionMessage) { var ex = await ThrowsArgumentAsync(testCode, paramName); VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true); return ex; } /// /// Verifies that the code throws an ArgumentException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsArgument(Func testCode, string paramName, bool allowDerivedExceptions = false) { var ex = Throws(testCode, allowDerivedExceptions); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the code throws an ArgumentNullException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentNullException ThrowsArgumentNull(Action testCode, string paramName) { var ex = Throws(testCode, allowDerivedExceptions: false); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the throws an . /// /// A delegate to the code to be tested. /// The name of the parameter that should throw the exception. /// A that on completion returns the exception that was thrown. public static async Task ThrowsArgumentNullAsync(Func testCode, string paramName) { var ex = await ThrowsAsync(testCode); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot /// be null or empty. /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName) { return Throws(testCode, "Value cannot be null or empty." + GetParameterMessage(paramName), allowDerivedExceptions: false); } /// /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot /// be null or empty string. /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsArgumentNullOrEmptyString(Action testCode, string paramName) { return ThrowsArgument(testCode, paramName, "Value cannot be null or an empty string.", allowDerivedExceptions: true); } /// /// Verifies that the code throws an ArgumentOutOfRangeException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The exception message to verify /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The actual value provided /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentOutOfRangeException ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false, object actualValue = null) { if (exceptionMessage != null) { exceptionMessage = exceptionMessage + GetParameterMessage(paramName); if (actualValue != null) { exceptionMessage += String.Format(CultureReplacer.DefaultCulture, "\r\nActual value was {0}.", actualValue); } } var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the throws an . /// /// A delegate to the code to be tested. /// The name of the parameter that should throw the exception. /// The exception message to verify (or a portion of the expected message). /// The actual value passed in for . /// A that on completion returns the exception that was thrown. public static async Task ThrowsArgumentOutOfRangeAsync( Func testCode, string paramName, string exceptionMessage, object actualValue = null) { if (exceptionMessage != null) { exceptionMessage = exceptionMessage + GetParameterMessage(paramName); if (actualValue != null) { exceptionMessage += String.Format(CultureReplacer.DefaultCulture, "\r\nActual value was {0}.", actualValue); } } var ex = await ThrowsAsync(testCode, exceptionMessage, partialMatch: true); if (paramName != null) { Equal(paramName, ex.ParamName); } return ex; } /// /// Verifies that the code throws an with the expected message that indicates that /// the value must be greater than the given . /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The actual value provided. /// The expected limit value. /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentOutOfRangeException ThrowsArgumentGreaterThan(Action testCode, string paramName, string value, object actualValue = null) { return ThrowsArgumentOutOfRange( testCode, paramName, String.Format(CultureReplacer.DefaultCulture, "Value must be greater than {0}.", value), false, actualValue); } /// /// Verifies that the code throws an with the expected message that indicates that /// the value must be greater than or equal to the given value. /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The expected limit value. /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentOutOfRangeException ThrowsArgumentGreaterThanOrEqualTo(Action testCode, string paramName, string value, object actualValue = null) { return ThrowsArgumentOutOfRange( testCode, paramName, String.Format(CultureReplacer.DefaultCulture, "Value must be greater than or equal to {0}.", value), false, actualValue); } /// /// Verifies that the throws an . /// /// A delegate to the code to be tested. /// The name of the parameter that should throw the exception. /// The minimum allowed value for . /// The actual value passed in for . /// A that on completion returns the exception that was thrown. public static Task ThrowsArgumentGreaterThanOrEqualToAsync( Func testCode, string paramName, string value, object actualValue = null) { return ThrowsArgumentOutOfRangeAsync( testCode, paramName, String.Format(CultureReplacer.DefaultCulture, "Value must be greater than or equal to {0}.", value), actualValue); } /// /// Verifies that the code throws an with the expected message that indicates that /// the value must be less than the given . /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The actual value provided. /// The expected limit value. /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentOutOfRangeException ThrowsArgumentLessThan(Action testCode, string paramName, string maxValue, object actualValue = null) { return ThrowsArgumentOutOfRange( testCode, paramName, String.Format(CultureReplacer.DefaultCulture, "Value must be less than {0}.", maxValue), false, actualValue); } /// /// Verifies that the code throws an with the expected message that indicates that /// the value must be less than or equal to the given . /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The actual value provided. /// The expected limit value. /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentOutOfRangeException ThrowsArgumentLessThanOrEqualTo(Action testCode, string paramName, string maxValue, object actualValue = null) { return ThrowsArgumentOutOfRange( testCode, paramName, String.Format(CultureReplacer.DefaultCulture, "Value must be less than or equal to {0}.", maxValue), false, actualValue); } #if !NETCOREAPP /// /// Verifies that the code throws an HttpException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The exception message to verify /// The expected HTTP status code of the exception /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static HttpException ThrowsHttpException(Action testCode, string exceptionMessage, int httpCode, bool allowDerivedExceptions = false) { var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions); Equal(httpCode, ex.GetHttpCode()); return ex; } #endif /// /// Verifies that the code throws an InvalidEnumArgumentException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the parameter that should throw the exception /// The expected invalid value that should appear in the message /// The type of the enumeration /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false) { string message = String.Format(CultureReplacer.DefaultCulture, "The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.{3}", paramName, invalidValue, enumType.Name, GetParameterMessage(paramName)); #if Testing_NetStandard1_3 // InvalidEnumArgumentException not available in netstandard1.3. return Throws(testCode, message, allowDerivedExceptions); #else return Throws(testCode, message, allowDerivedExceptions); #endif } /// /// Verifies that the code throws an HttpException (or optionally any exception which derives from it). /// /// A delegate to the code to be tested /// The name of the object that was dispose /// Pass true to allow exceptions which derive from TException; pass false, otherwise /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown public static ObjectDisposedException ThrowsObjectDisposed(Action testCode, string objectName, bool allowDerivedExceptions = false) { var ex = Throws(testCode, allowDerivedExceptions); if (objectName != null) { Equal(objectName, ex.ObjectName); } return ex; } /// /// Verifies that an exception of the given type is thrown. /// /// The type of the exception expected to be thrown /// A delegate to the code to be tested /// The exception that was thrown, when successful /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown /// /// Unlike other Throws* methods, this method does not enforce running the exception delegate with a known Thread Culture. /// public static new async Task ThrowsAsync(Func testCode) where TException : Exception { Exception exception = null; try { // The 'testCode' Task might execute asynchronously in a different thread making it hard to enforce the thread culture. // The correct way to verify exception messages in such a scenario would be to run the task synchronously inside of a // culture enforced block. await testCode(); } catch (Exception ex) { exception = ex; } VerifyException(typeof(TException), exception); return (TException)exception; } /// /// Verifies that the throws a . /// /// The type of expected to be thrown. /// A delegate to the code to be tested. /// The exception message to verify (or a portion of the expected message). /// /// If is null, ignores this parameter. Otherwise if this parameter /// is true, verifies the exception message contains . Otherwise, /// verifies the exception message exactly matches . /// /// A that on completion returns the exception that was thrown. public static async Task ThrowsAsync( Func testCode, string exceptionMessage, bool partialMatch = false) where TException : Exception { var ex = await ThrowsAsync(testCode); VerifyExceptionMessage(ex, exceptionMessage, partialMatch); return ex; } private static string GetParameterMessage(string parameterName) { #if NETCOREAPP3_1_OR_GREATER return " (Parameter '" + parameterName + "')"; #else return Environment.NewLine + "Parameter name: " + parameterName; #endif } // We've re-implemented all the xUnit.net Throws code so that we can get this // updated implementation of RecordException which silently unwraps any instances // of AggregateException. In addition to unwrapping exceptions, this method ensures // that tests are executed in with a known set of Culture and UICulture. This prevents // tests from failing when executed on a non-English machine. private static new Exception RecordException(Action testCode) { try { using (new CultureReplacer()) { testCode(); } return null; } catch (Exception exception) { return UnwrapException(exception); } } private static Exception UnwrapException(Exception exception) { AggregateException aggEx = exception as AggregateException; if (aggEx != null) { return aggEx.GetBaseException(); } return exception; } private static Exception VerifyException(Type exceptionType, Exception exception) { if (exception == null) { throw new ThrowsException(exceptionType); } else if (exceptionType != exception.GetType()) { throw new ThrowsException(exceptionType, exception); } return exception; } private static void VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false) { if (expectedMessage != null) { if (!partialMatch) { Equal(expectedMessage, exception.Message); } else { Contains(expectedMessage, exception.Message); } } } // Custom ThrowsException so we can filter the stack trace. [Serializable] private class ThrowsException : Xunit.Sdk.ThrowsException { public ThrowsException(Type type) : base(type) { } public ThrowsException(Type type, Exception ex) : base(type, ex) { } public override string StackTrace { get { return ExceptionUtility.FilterStackTrace(base.StackTrace); } } } } } ================================================ FILE: test/Microsoft.TestCommon/ExceptionUtility.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; // Variation of Xunit.Sdk.ExceptionUtility which helps with Exceptions and their stack traces. namespace Microsoft.TestCommon { public static class ExceptionUtility { public static string FilterStackTrace(string stack) { if (stack == null) { return null; } var results = new List(); foreach (var line in SplitLines(stack)) { var trimmedLine = line.TrimStart(); if (!ExcludeStackFrame(trimmedLine)) { results.Add(line); } } return string.Join(Environment.NewLine, results); } private static IEnumerable SplitLines(string input) { while (true) { var index = input.IndexOf(Environment.NewLine, StringComparison.Ordinal); if (index < 0) { yield return input; break; } yield return input.Substring(0, index); input = input.Substring(index + Environment.NewLine.Length); } } private static bool ExcludeStackFrame(string stackFrame) { if (stackFrame.StartsWith("at Microsoft.TestCommon.Assert.", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } } } ================================================ FILE: test/Microsoft.TestCommon/FactAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit.Sdk; namespace Microsoft.TestCommon { /// /// An override of that provides extended capabilities. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [XunitTestCaseDiscoverer("Microsoft.TestCommon.FactDiscoverer", "Microsoft.TestCommon")] public class FactAttribute : Xunit.FactAttribute { /// /// Instantiates a new instance of . /// public FactAttribute() { Platforms = Platform.All; PlatformJustification = "Unsupported platform (test runs on {0}, current platform is {1})"; } /// /// Gets or set the platforms that the unit test is compatible with. Defaults to /// . /// public Platform Platforms { get; set; } /// /// Gets or sets the platform skipping justification. This message can receive /// the supported platforms as {0}, and the current platform as {1}. /// public string PlatformJustification { get; set; } } } ================================================ FILE: test/Microsoft.TestCommon/FactDiscoverer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.TestCommon { /// /// An override of that provides extended capabilities. /// class FactDiscoverer : Xunit.Sdk.FactDiscoverer { private readonly IMessageSink _diagnosticMessageSink; /// /// Instantiates a new instance. /// /// The used to send diagnostic messages. public FactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { _diagnosticMessageSink = diagnosticMessageSink; } /// /// Gets the platform that the unit test is currently running on. /// protected Platform Platform { get { return PlatformInfo.Platform; } } /// public override IEnumerable Discover( ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) { var baseCases = base.Discover(discoveryOptions, testMethod, factAttribute); if (!String.IsNullOrEmpty(factAttribute.GetNamedArgument("Skip"))) { // No need to change skipped tests. return baseCases; } var platforms = factAttribute.GetNamedArgument("Platforms"); if ((platforms & Platform) != 0) { // No need to change tests that should run on the current platform. return baseCases; } // Base implementation always returns a single test case. var baseCase = baseCases.FirstOrDefault(); Contract.Assert(baseCase != null); if (baseCase is ExecutionErrorTestCase) { // No need to change an erroneous test. return baseCases; } if (!String.IsNullOrEmpty(baseCase.SkipReason)) { // No need to change a skipped test. Covered to protect against changes in the base class. return baseCases; } // Replace test with its skipped equivalent. var platformJustification = factAttribute.GetNamedArgument("PlatformJustification"); var skipReason = String.Format(platformJustification, platforms.ToString().Replace(", ", " | "), Platform); var testCase = new SkippedXunitTestCase( _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, skipReason, baseCase.TestMethod, baseCase.TestMethodArguments); return new[] { testCase }; } } } ================================================ FILE: test/Microsoft.TestCommon/ForceGCAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; namespace Microsoft.TestCommon { public class ForceGCAttribute : Xunit.Sdk.BeforeAfterTestAttribute { public override void After(MethodInfo methodUnderTest) { GC.Collect(99); GC.Collect(99); GC.Collect(99); } } } ================================================ FILE: test/Microsoft.TestCommon/InlineDataAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Reflection; using Xunit.Sdk; namespace Microsoft.TestCommon { // Xunit.InlineDataAttribute is unfortunately sealed. Delegate to an instance to avoid duplicating its code. [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] [DataDiscoverer("Xunit.Sdk.InlineDataDiscoverer", "xunit.core")] public class InlineDataAttribute : Xunit.Sdk.DataAttribute { Xunit.InlineDataAttribute _inner; public InlineDataAttribute(params object[] dataValues) : base() { _inner = new Xunit.InlineDataAttribute(dataValues); } public override IEnumerable GetData(MethodInfo testMethod) { return _inner.GetData(testMethod); } } } ================================================ FILE: test/Microsoft.TestCommon/MatrixTheoryDataSet.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; namespace Microsoft.TestCommon { public class MatrixTheoryDataSet : TheoryDataSet { public MatrixTheoryDataSet(IEnumerable data1, IEnumerable data2) { Contract.Assert(data1 != null && data1.Any()); Contract.Assert(data2 != null && data2.Any()); foreach (T1 t1 in data1) { foreach (T2 t2 in data2) { Add(t1, t2); } } } } } ================================================ FILE: test/Microsoft.TestCommon/MemberHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using Microsoft.TestCommon; namespace System.Web.TestUtil { public static class MemberHelper { private static ConstructorInfo GetConstructorInfo(object instance, Type[] parameterTypes) { if (instance == null) { throw new ArgumentNullException("instance"); } ConstructorInfo constructorInfo = instance.GetType().GetConstructor(parameterTypes); if (constructorInfo == null) { throw new ArgumentException(String.Format( "A matching constructor on type '{0}' could not be found.", instance.GetType().FullName)); } return constructorInfo; } private static EventInfo GetEventInfo(object instance, string eventName) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(eventName)) { throw new ArgumentException("An event must be specified.", "eventName"); } EventInfo eventInfo = instance.GetType().GetEvent(eventName); if (eventInfo == null) { throw new ArgumentException(String.Format( "An event named '{0}' on type '{1}' could not be found.", eventName, instance.GetType().FullName)); } return eventInfo; } private static MethodInfo GetMethodInfo(object instance, string methodName, Type[] types = null, MethodAttributes attrs = MethodAttributes.Public) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(methodName)) { throw new ArgumentException("A method must be specified.", "methodName"); } MethodInfo methodInfo; if (types != null) { methodInfo = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); } else { methodInfo = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (methodInfo == null) { throw new ArgumentException(String.Format( "A method named '{0}' on type '{1}' could not be found.", methodName, instance.GetType().FullName)); } if ((methodInfo.Attributes & attrs) != attrs) { throw new ArgumentException(String.Format( "Method '{0}' on type '{1}' with attributes '{2}' does not match the attributes '{3}'.", methodName, instance.GetType().FullName, methodInfo.Attributes, attrs)); } return methodInfo; } private static PropertyInfo GetPropertyInfo(object instance, string propertyName) { if (instance == null) { throw new ArgumentNullException("instance"); } if (String.IsNullOrEmpty(propertyName)) { throw new ArgumentException("A property must be specified.", "propertyName"); } PropertyInfo propInfo = instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (propInfo == null) { throw new ArgumentException(String.Format( "A property named '{0}' on type '{1}' could not be found.", propertyName, instance.GetType().FullName)); } return propInfo; } private static void TestAttribute(MemberInfo memberInfo, TAttribute attributeValue) where TAttribute : Attribute { object[] attrs = memberInfo.GetCustomAttributes(typeof(TAttribute), true); if (attributeValue == null) { Assert.True(attrs.Length == 0, "Member should not have an attribute of type " + typeof(TAttribute)); } else { Assert.True(attrs != null && attrs.Length > 0, "Member does not have an attribute of type " + typeof(TAttribute)); Assert.Equal(attributeValue, attrs[0]); } } public static void TestBooleanProperty(object instance, string propertyName, bool initialValue, bool testDefaultValue) { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValue) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } TestPropertyValue(instance, propertyName, true); TestPropertyValue(instance, propertyName, false); } public static void TestDefaultValue(object instance, string propertyName) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); object initialValue = propInfo.GetValue(instance, null); TestAttribute(propInfo, new DefaultValueAttribute(initialValue)); } public static void TestEvent(object instance, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs { EventInfo eventInfo = GetEventInfo(instance, eventName); // Assert category "Action" TestAttribute(eventInfo, new CategoryAttribute("Action")); // Call protected method with no event handlers, assert no error MethodInfo methodInfo = GetMethodInfo(instance, "On" + eventName, attrs: MethodAttributes.Family | MethodAttributes.Virtual); methodInfo.Invoke(instance, new object[] { eventArgs }); // Attach handler, call method, assert fires once List eventHandlerArgs = new List(); EventHandler handler = new EventHandler(delegate(object sender, TEventArgs t) { eventHandlerArgs.Add(sender); eventHandlerArgs.Add(t); }); eventInfo.AddEventHandler(instance, handler); methodInfo.Invoke(instance, new object[] { eventArgs }); Assert.Equal(new[] { instance, eventArgs }, eventHandlerArgs.ToArray()); // Detach handler, call method, assert not fired eventHandlerArgs = new List(); eventInfo.RemoveEventHandler(instance, handler); methodInfo.Invoke(instance, new object[] { eventArgs }); Assert.Empty(eventHandlerArgs); } public static void TestGetPropertyValue(object instance, string propertyName, object valueToCheck) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToCheck, value); } public static void TestEnumProperty(object instance, string propertyName, TEnumValue initialValue, bool testDefaultValue) { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValue) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Values are sorted numerically TEnumValue[] values = (TEnumValue[])Enum.GetValues(propInfo.PropertyType); // Assert get/set works for all valid enum values foreach (TEnumValue value in values) { TestPropertyValue(instance, propertyName, value); } // Assert ArgumentOutOfRangeException is thrown for value one less than smallest // enum value, and one more than largest enum value var targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[0]) - 1, null)); Assert.IsType(targetException.InnerException); targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[values.Length - 1]) + 1, null)); Assert.IsType(targetException.InnerException); } public static void TestInt32Property(object instance, string propertyName, int value1, int value2) { TestPropertyValue(instance, propertyName, value1); TestPropertyValue(instance, propertyName, value2); } public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Set to explicit property propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToSet, value); // Set to null propInfo.SetValue(instance, null, null); value = propInfo.GetValue(instance, null); Assert.IsAssignableFrom(propInfo.PropertyType, value); } public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet, object defaultValue) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); // Set to explicit property propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Same(valueToSet, value); // Set to null propInfo.SetValue(instance, null, null); value = propInfo.GetValue(instance, null); Assert.Equal(defaultValue, value); } public static void TestPropertyValue(object instance, string propertyName, object value) { TestPropertyValue(instance, propertyName, value, value); } public static void TestPropertyValue(object instance, string propertyName, object valueToSet, object valueToCheck) { PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); propInfo.SetValue(instance, valueToSet, null); object value = propInfo.GetValue(instance, null); Assert.Equal(valueToCheck, value); } public static void TestStringParams(object instance, Type[] constructorParameterTypes, object[] parameters) { ConstructorInfo ctor = GetConstructorInfo(instance, constructorParameterTypes); TestStringParams(ctor, instance, parameters); } public static void TestStringParams(object instance, string methodName, object[] parameters) { TestStringParams(instance, methodName, null, parameters); } public static void TestStringParams(object instance, string methodName, Type[] types, object[] parameters) { MethodInfo method = GetMethodInfo(instance, methodName, types); TestStringParams(method, instance, parameters); } private static void TestStringParams(MethodBase method, object instance, object[] parameters) { ParameterInfo[] parameterInfos = method.GetParameters(); foreach (ParameterInfo parameterInfo in parameterInfos) { if (parameterInfo.ParameterType == typeof(String)) { object originalParameter = parameters[parameterInfo.Position]; parameters[parameterInfo.Position] = null; Assert.ThrowsArgumentNullOrEmpty( delegate() { try { method.Invoke(instance, parameters); } catch (TargetInvocationException e) { throw e.InnerException; } }, parameterInfo.Name); parameters[parameterInfo.Position] = String.Empty; Assert.ThrowsArgumentNullOrEmpty( delegate() { try { method.Invoke(instance, parameters); } catch (TargetInvocationException e) { throw e.InnerException; } }, parameterInfo.Name); parameters[parameterInfo.Position] = originalParameter; } } } public static void TestStringProperty(object instance, string propertyName, string initialValue, bool testDefaultValueAttribute = false, bool allowNullAndEmpty = true, string nullAndEmptyReturnValue = "") { // Assert initial value TestGetPropertyValue(instance, propertyName, initialValue); if (testDefaultValueAttribute) { // Assert DefaultValue attribute matches inital value TestDefaultValue(instance, propertyName); } if (allowNullAndEmpty) { // Assert get/set works for null TestPropertyValue(instance, propertyName, null, nullAndEmptyReturnValue); // Assert get/set works for String.Empty TestPropertyValue(instance, propertyName, String.Empty, nullAndEmptyReturnValue); } else { Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, propertyName, null); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, propertyName, String.Empty); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); } // Assert get/set works for arbitrary value TestPropertyValue(instance, propertyName, "TestValue"); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/CommonUnitTestDataSets.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.ObjectModel; using Microsoft.TestCommon.Types; namespace Microsoft.TestCommon { public class CommonUnitTestDataSets { public static ValueTypeTestData Chars { get { return TestData.CharTestData; } } public static ValueTypeTestData Ints { get { return TestData.IntTestData; } } public static ValueTypeTestData Uints { get { return TestData.UintTestData; } } public static ValueTypeTestData Shorts { get { return TestData.ShortTestData; } } public static ValueTypeTestData Ushorts { get { return TestData.UshortTestData; } } public static ValueTypeTestData Longs { get { return TestData.LongTestData; } } public static ValueTypeTestData Ulongs { get { return TestData.UlongTestData; } } public static ValueTypeTestData Bytes { get { return TestData.ByteTestData; } } public static ValueTypeTestData SBytes { get { return TestData.SByteTestData; } } public static ValueTypeTestData Bools { get { return TestData.BoolTestData; } } public static ValueTypeTestData Doubles { get { return TestData.DoubleTestData; } } public static ValueTypeTestData Floats { get { return TestData.FloatTestData; } } public static ValueTypeTestData DateTimes { get { return TestData.DateTimeTestData; } } public static ValueTypeTestData Decimals { get { return TestData.DecimalTestData; } } public static ValueTypeTestData TimeSpans { get { return TestData.TimeSpanTestData; } } public static ValueTypeTestData Guids { get { return TestData.GuidTestData; } } public static ValueTypeTestData DateTimeOffsets { get { return TestData.DateTimeOffsetTestData; } } public static ValueTypeTestData SimpleEnums { get { return TestData.SimpleEnumTestData; } } public static ValueTypeTestData LongEnums { get { return TestData.LongEnumTestData; } } public static ValueTypeTestData FlagsEnums { get { return TestData.FlagsEnumTestData; } } public static TestData EmptyStrings { get { return TestData.EmptyStrings; } } public static RefTypeTestData Strings { get { return TestData.StringTestData; } } public static TestData NonNullEmptyStrings { get { return TestData.NonNullEmptyStrings; } } public static RefTypeTestData ISerializableTypes { get { return TestData.ISerializableTypeTestData; } } public static ReadOnlyCollection ValueTypeTestDataCollection { get { return TestData.ValueTypeTestDataCollection; } } public static ReadOnlyCollection RefTypeTestDataCollection { get { return TestData.RefTypeTestDataCollection; } } public static ReadOnlyCollection ValueAndRefTypeTestDataCollection { get { return TestData.ValueAndRefTypeTestDataCollection; } } public static ReadOnlyCollection RepresentativeValueAndRefTypeTestDataCollection { get { return TestData.RepresentativeValueAndRefTypeTestDataCollection; } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/RefTypeTestData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; namespace Microsoft.TestCommon { public class RefTypeTestData : TestData where T : class { private Func> testDataProvider; private Func> derivedTypeTestDataProvider; private Func> knownTypeTestDataProvider; public RefTypeTestData(Func> testDataProvider) { if (testDataProvider == null) { throw new ArgumentNullException("testDataProvider"); } this.testDataProvider = testDataProvider; this.RegisterTestDataVariation(TestDataVariations.WithNull, this.Type, GetNullTestData); } public RefTypeTestData( Func> testDataProvider, Func> derivedTypeTestDataProvider, Func> knownTypeTestDataProvider) : this(testDataProvider) { this.derivedTypeTestDataProvider = derivedTypeTestDataProvider; if (this.derivedTypeTestDataProvider != null) { this.RegisterTestDataVariation(TestDataVariations.AsDerivedType, this.Type, this.GetTestDataAsDerivedType); } this.knownTypeTestDataProvider = knownTypeTestDataProvider; if (this.knownTypeTestDataProvider != null) { this.RegisterTestDataVariation(TestDataVariations.AsKnownType, this.Type, this.GetTestDataAsDerivedKnownType); } } public T GetNullTestData() { return null; } public IEnumerable GetTestDataAsDerivedType() { if (this.derivedTypeTestDataProvider != null) { return this.derivedTypeTestDataProvider(); } return Enumerable.Empty(); } public IEnumerable GetTestDataAsDerivedKnownType() { if (this.knownTypeTestDataProvider != null) { return this.knownTypeTestDataProvider(); } return Enumerable.Empty(); } protected override IEnumerable GetTypedTestData() { return this.testDataProvider(); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Microsoft.TestCommon.Types; namespace Microsoft.TestCommon { /// /// A base class for test data. A instance is associated with a given type, and the instance can /// provide instances of the given type to use as data in tests. The same instance can also provide instances /// of types related to the given type, such as a of the type. See the enum for all the /// variations of test data that a instance can provide. /// public abstract class TestData { /// /// Common for a . /// public static readonly ValueTypeTestData CharTestData = new ValueTypeTestData('a', Char.MinValue, Char.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData IntTestData = new ValueTypeTestData(-1, 0, 1, Int32.MinValue, Int32.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData UintTestData = new ValueTypeTestData(0, 1, UInt32.MinValue, UInt32.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData ShortTestData = new ValueTypeTestData(-1, 0, 1, Int16.MinValue, Int16.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData UshortTestData = new ValueTypeTestData(0, 1, UInt16.MinValue, UInt16.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData LongTestData = new ValueTypeTestData(-1, 0, 1, Int64.MinValue, Int64.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData UlongTestData = new ValueTypeTestData(0, 1, UInt64.MinValue, UInt64.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData ByteTestData = new ValueTypeTestData(0, 1, Byte.MinValue, Byte.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData SByteTestData = new ValueTypeTestData(-1, 0, 1, SByte.MinValue, SByte.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData BoolTestData = new ValueTypeTestData(true, false); /// /// Common for a . /// public static readonly ValueTypeTestData DoubleTestData = new ValueTypeTestData( -1.0, 0.0, 1.0, double.MinValue, double.MaxValue, double.PositiveInfinity, double.NegativeInfinity); /// /// Common for a . /// public static readonly ValueTypeTestData FloatTestData = new ValueTypeTestData( -1.0f, 0.0f, 1.0f, float.MinValue, float.MaxValue, float.PositiveInfinity, float.NegativeInfinity); /// /// Common for a . /// public static readonly ValueTypeTestData DecimalTestData = new ValueTypeTestData( -1M, 0M, 1M, decimal.MinValue, decimal.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData DateTimeTestData = new ValueTypeTestData( DateTime.Now, DateTime.UtcNow, DateTime.MaxValue, DateTime.MinValue); /// /// Common for a . /// public static readonly ValueTypeTestData TimeSpanTestData = new ValueTypeTestData( TimeSpan.MinValue, TimeSpan.MaxValue); /// /// Common for a . /// public static readonly ValueTypeTestData GuidTestData = new ValueTypeTestData( Guid.NewGuid(), Guid.Empty); /// /// Common for a . /// public static readonly ValueTypeTestData DateTimeOffsetTestData = new ValueTypeTestData( DateTimeOffset.MaxValue, DateTimeOffset.MinValue, new DateTimeOffset(DateTime.Now)); /// /// Common for an enum. /// public static readonly ValueTypeTestData SimpleEnumTestData = new ValueTypeTestData( SimpleEnum.First, SimpleEnum.Second, SimpleEnum.Third); /// /// Common for an enum implemented with a . /// public static readonly ValueTypeTestData LongEnumTestData = new ValueTypeTestData( LongEnum.FirstLong, LongEnum.SecondLong, LongEnum.ThirdLong); /// /// Common for an enum decorated with a . /// public static readonly ValueTypeTestData FlagsEnumTestData = new ValueTypeTestData( FlagsEnum.One, FlagsEnum.Two, FlagsEnum.Four); /// /// Expected permutations of non supported file paths. /// public static readonly TestData NotSupportedFilePaths = new RefTypeTestData(() => new List() { "cc:\\a\\b", }); /// /// Expected permutations of invalid file paths. /// public static readonly TestData InvalidNonNullFilePaths = new RefTypeTestData(() => new List() { String.Empty, "", " ", " ", "\t\t \n ", "c:\\ab", "c:\\a\"b", "c:\\a\tb", "c:\\a|b", "c:\\a\bb", "c:\\a\0b", }); /// /// All expected permutations of an empty string. /// public static readonly TestData NonNullEmptyStrings = new RefTypeTestData(() => new List() { String.Empty, " ", "\t\r\n" }); /// /// All expected permutations of an empty string. /// public static readonly TestData EmptyStrings = new RefTypeTestData(() => new List() { null, String.Empty, " ", "\t\r\n" }); /// /// Common for a . /// public static readonly RefTypeTestData StringTestData = new RefTypeTestData(() => new List() { "", " ", // one space " ", // multiple spaces " data ", // leading and trailing whitespace "\t\t \n ", "Some String!"}); /// /// Common for a class that implements . /// public static readonly RefTypeTestData ISerializableTypeTestData = new RefTypeTestData( ISerializableType.GetTestData); /// /// A read-only collection of value type test data. /// public static readonly ReadOnlyCollection ValueTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { CharTestData, IntTestData, UintTestData, ShortTestData, UshortTestData, LongTestData, UlongTestData, ByteTestData, SByteTestData, BoolTestData, DoubleTestData, FloatTestData, DecimalTestData, TimeSpanTestData, GuidTestData, DateTimeOffsetTestData, SimpleEnumTestData, LongEnumTestData, FlagsEnumTestData}); /// /// A read-only collection of reference type test data. /// public static readonly ReadOnlyCollection RefTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { StringTestData, ISerializableTypeTestData}); /// /// A read-only collection of value and reference type test data. /// public static readonly ReadOnlyCollection ValueAndRefTypeTestDataCollection = new ReadOnlyCollection( ValueTypeTestDataCollection.Concat(RefTypeTestDataCollection).ToList()); /// /// A read-only collection of representative values and reference type test data. /// Uses where exhaustive coverage is not required. /// public static readonly ReadOnlyCollection RepresentativeValueAndRefTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { IntTestData, BoolTestData, SimpleEnumTestData, StringTestData, }); private Dictionary registeredTestDataVariations; /// /// Initializes a new instance of the class. /// /// The type associated with the instance. protected TestData(Type type) { if (type.ContainsGenericParameters) { throw new InvalidOperationException("Only closed generic types are supported."); } this.Type = type; this.registeredTestDataVariations = new Dictionary(); } /// /// Gets the type associated with the instance. /// public Type Type { get; private set; } /// /// Gets the supported test data variations. /// /// public IEnumerable GetSupportedTestDataVariations() { return this.registeredTestDataVariations.Keys; } /// /// Gets the related type for the given test data variation or returns null if the instance /// doesn't support the given variation. /// /// The test data variation with which to create the related . /// The related for the as given by the test data variation. /// /// For example, if the given was created for test data and the varation parameter /// was then the returned type would be . /// public Type GetAsTypeOrNull(TestDataVariations variation) { TestDataVariationProvider testDataVariation = null; if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation)) { return testDataVariation.Type; } return null; } /// /// Gets test data for the given test data variation or returns null if the instance /// doesn't support the given variation. /// /// The test data variation with which to create the related test data. /// Test data of the type specified by the method. public object GetAsTestDataOrNull(TestDataVariations variation) { TestDataVariationProvider testDataVariation = null; if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation)) { return testDataVariation.TestDataProvider(); } return null; } /// /// Allows derived classes to register a that will /// provide test data for a given variation. /// /// The variation with which to register the r. /// The type of the test data created by the /// A that will provide test data. protected void RegisterTestDataVariation(TestDataVariations variation, Type type, Func testDataProvider) { this.registeredTestDataVariations.Add(variation, new TestDataVariationProvider(type, testDataProvider)); } private class TestDataVariationProvider { public TestDataVariationProvider(Type type, Func testDataProvider) { this.Type = type; this.TestDataProvider = testDataProvider; } public Func TestDataProvider { get; private set; } public Type Type { get; private set; } } } /// /// A generic base class for test data. /// /// The type associated with the test data. public abstract class TestData : TestData, IEnumerable { private static readonly Type OpenIEnumerableType = typeof(IEnumerable<>); private static readonly Type OpenListType = typeof(List<>); private static readonly Type OpenIQueryableType = typeof(IQueryable<>); private static readonly Type OpenDictionaryType = typeof(Dictionary<,>); private static readonly Type OpenTestDataHolderType = typeof(TestDataHolder<>); /// /// Initializes a new instance of the class. /// protected TestData() : base(typeof(T)) { Type[] typeParams = new Type[] { this.Type }; Type[] dictionaryTypeParams = new Type[] { typeof(string), this.Type }; Type arrayType = this.Type.MakeArrayType(); Type listType = OpenListType.MakeGenericType(typeParams); Type iEnumerableType = OpenIEnumerableType.MakeGenericType(typeParams); Type iQueryableType = OpenIQueryableType.MakeGenericType(typeParams); Type dictionaryType = OpenDictionaryType.MakeGenericType(dictionaryTypeParams); Type testDataHolderType = OpenTestDataHolderType.MakeGenericType(typeParams); this.RegisterTestDataVariation(TestDataVariations.AsInstance, this.Type, () => GetTypedTestData()); this.RegisterTestDataVariation(TestDataVariations.AsArray, arrayType, GetTestDataAsArray); this.RegisterTestDataVariation(TestDataVariations.AsIEnumerable, iEnumerableType, GetTestDataAsIEnumerable); this.RegisterTestDataVariation(TestDataVariations.AsIQueryable, iQueryableType, GetTestDataAsIQueryable); this.RegisterTestDataVariation(TestDataVariations.AsList, listType, GetTestDataAsList); this.RegisterTestDataVariation(TestDataVariations.AsDictionary, dictionaryType, GetTestDataAsDictionary); this.RegisterTestDataVariation(TestDataVariations.AsClassMember, testDataHolderType, GetTestDataInHolder); } public IEnumerator GetEnumerator() { return (IEnumerator)this.GetTypedTestData().ToList().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)this.GetTypedTestData().ToList().GetEnumerator(); } /// /// Gets the test data as an array. /// /// An array of test data of the given type. public T[] GetTestDataAsArray() { return this.GetTypedTestData().ToArray(); } /// /// Gets the test data as a . /// /// A of test data of the given type. public List GetTestDataAsList() { return this.GetTypedTestData().ToList(); } /// /// Gets the test data as an . /// /// An of test data of the given type. public IEnumerable GetTestDataAsIEnumerable() { return this.GetTypedTestData().AsEnumerable(); } /// /// Gets the test data as an . /// /// An of test data of the given type. public IQueryable GetTestDataAsIQueryable() { return this.GetTypedTestData().AsQueryable(); } public Dictionary GetTestDataAsDictionary() { // Some TestData collections contain duplicates e.g. UintTestData contains both 0 and UInt32.MinValue. // Therefore use dictionaryKey, not _unused.ToString(). Do not reuse key to keep dictionaries consistent // if enumerated multiple times. int dictionaryKey = 0; return this.GetTypedTestData().ToDictionary(_unused => (dictionaryKey++).ToString()); } public IEnumerable> GetTestDataInHolder() { return this.GetTypedTestData().Select(value => new TestDataHolder { V1 = value, }); } /// /// Must be implemented by derived types to return test data of the given type. /// /// Test data of the given type. protected abstract IEnumerable GetTypedTestData(); } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataHolder.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; namespace Microsoft.TestCommon { /// /// Equatable class wrapping a single instance of type . Equatable to ease test assertions. /// /// The to wrap. public class TestDataHolder : IEquatable> { public T V1 { get; set; } bool IEquatable>.Equals(TestDataHolder other) { if (other == null) { return false; } return Object.Equals(V1, other.V1); } public override bool Equals(object obj) { TestDataHolder that = obj as TestDataHolder; return ((IEquatable>)this).Equals(that); } public override int GetHashCode() { if (typeof(ValueType).IsAssignableFrom(typeof(T)) || V1 != null) { return V1.GetHashCode(); } else { return 0; } } public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{{ V1: '{0}' }}", V1); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataVariations.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon { /// /// An flags enum that can be used to indicate different variations of a given /// instance. /// [Flags] public enum TestDataVariations { /// /// An individual instance of a given type. /// AsInstance = 0x1, /// /// An individual instance of a type that derives from a given type. /// AsDerivedType = 0x2, /// /// An individual instance of a given type that has a property value /// that is a known type of the declared property type. /// AsKnownType = 0x4, /// /// A instance of a given type. Only applies to /// instances of . /// AsNullable = 0x8, /// /// An instance of a of a given type. /// AsList = 0x10, /// /// An instance of a array of the type. /// AsArray = 0x20, /// /// An instance of an of a given type. /// AsIEnumerable = 0x40, /// /// An instance of an of a given type. /// AsIQueryable = 0x80, /// /// An instance of a DataContract type in which a given type is a member. /// AsDataMember = 0x100, /// /// An instance of a type in which a given type is decorated with a /// . /// AsXmlElementProperty = 0x200, /// /// An instance of a of a given /// type. /// AsDictionary = 0x400, /// /// Add a null instance of the given type to the data set. This variation is /// not included in or other variation masks. /// WithNull = 0x800, /// /// Individual instances of containing the given . This /// variation is not included in or other variation masks. /// AsClassMember = 0x1000, /// /// All of the flags for single instance variations of a given type. /// AllSingleInstances = AsInstance | AsDerivedType | AsKnownType | AsNullable, /// /// All of the flags for collection variations of a given type. /// AllCollections = AsList | AsArray | AsIEnumerable | AsIQueryable | AsDictionary, /// /// All of the flags for variations in which a given type is a property on another type. /// AllProperties = AsDataMember | AsXmlElementProperty, /// /// All of the flags for interface collection variations of a given type. /// AllInterfaces = AsIEnumerable | AsIQueryable, /// /// All of the flags except for the interface collection variations of a given type. /// AllNonInterfaces = All & ~AllInterfaces, /// /// All of the flags for all of the variations of a given type. /// All = AllSingleInstances | AllCollections | AllProperties } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/ValueTypeTestData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; namespace Microsoft.TestCommon { public class ValueTypeTestData : TestData where T : struct { private static readonly Type OpenNullableType = typeof(Nullable<>); private T[] testData; public ValueTypeTestData(params T[] testData) : base() { this.testData = testData; Type[] typeParams = new Type[] { this.Type }; this.RegisterTestDataVariation(TestDataVariations.WithNull, OpenNullableType.MakeGenericType(typeParams), GetNullTestData); this.RegisterTestDataVariation(TestDataVariations.AsNullable, OpenNullableType.MakeGenericType(typeParams), GetTestDataAsNullable); } public object GetNullTestData() { return null; } public IEnumerable> GetTestDataAsNullable() { return this.GetTypedTestData().Select(d => new Nullable(d)); } protected override IEnumerable GetTypedTestData() { return this.testData; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/GenericTypeAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Reflection; namespace Microsoft.TestCommon { /// /// MSTest assertion class to provide convenience and assert methods for generic types /// whose type parameters are not known at compile time. /// public class GenericTypeAssert { private static readonly GenericTypeAssert singleton = new GenericTypeAssert(); public static GenericTypeAssert Singleton { get { return singleton; } } /// /// Asserts the given is a generic type and creates a new /// bound generic type using . It then asserts there /// is a constructor that will accept and returns it. /// /// The unbound generic base type. /// The type of the single generic parameter to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The of that constructor which may be invoked to create that new generic type. public ConstructorInfo GetConstructor(Type genericBaseType, Type genericParameterType, params Type[] parameterTypes) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterType); Assert.NotNull(parameterTypes); Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); ConstructorInfo ctor = genericType.GetConstructor(parameterTypes); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); return ctor; } /// /// Asserts the given is a generic type and creates a new /// bound generic type using . It then asserts there /// is a constructor that will accept and returns it. /// /// The unbound generic base type. /// The types of the generic parameters to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The of that constructor which may be invoked to create that new generic type. public ConstructorInfo GetConstructor(Type genericBaseType, Type[] genericParameterTypes, params Type[] parameterTypes) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterTypes); Assert.NotNull(parameterTypes); Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); ConstructorInfo ctor = genericType.GetConstructor(parameterTypes); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); return ctor; } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The unbound generic base type. /// The type of the single generic parameter to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The list of values to supply to the constructor /// The instance created by calling that constructor. public object InvokeConstructor(Type genericBaseType, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) { ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterType, parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); return ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The unbound generic base type. /// The types of the generic parameters to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The list of values to supply to the constructor /// The instance created by calling that constructor. public object InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, Type[] parameterTypes, object[] parameterValues) { ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterTypes, parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); return ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from the types of . /// /// The unbound generic base type. /// The type of the single generic parameter to apply to create a bound generic type. /// The list of values to supply to the constructor. It must be possible to determine the /// The instance created by calling that constructor. public object InvokeConstructor(Type genericBaseType, Type genericParameterType, params object[] parameterValues) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterType); Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); ConstructorInfo ctor = FindConstructor(genericType, parameterValues); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); return ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from the types of . /// /// The unbound generic base type. /// The types of the generic parameters to apply to create a bound generic type. /// The list of values to supply to the constructor. It must be possible to determine the /// The instance created by calling that constructor. public object InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, params object[] parameterValues) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterTypes); Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); ConstructorInfo ctor = FindConstructor(genericType, parameterValues); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); return ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The type of object the constuctor is expected to yield. /// The unbound generic base type. /// The type of the single generic parameter to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The list of values to supply to the constructor /// An instance of type . public T InvokeConstructor(Type genericBaseType, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) { ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterType, parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); return (T)ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The type of object the constuctor is expected to yield. /// The unbound generic base type. /// The types of the generic parameters to apply to create a bound generic type. /// The list of parameter types for a constructor that must exist. /// The list of values to supply to the constructor /// An instance of type . public T InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, Type[] parameterTypes, object[] parameterValues) { ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterTypes, parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); return (T)ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The type of object the constuctor is expected to yield. /// The unbound generic base type. /// The type of the single generic parameter to apply to create a bound generic type. /// The list of values to supply to the constructor. It must be possible to determine the /// The instance created by calling that constructor. /// An instance of type . public T InvokeConstructor(Type genericBaseType, Type genericParameterType, params object[] parameterValues) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterType); Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); ConstructorInfo ctor = FindConstructor(genericType, parameterValues); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); return (T)ctor.Invoke(parameterValues); } /// /// Creates a new bound generic type and invokes the constructor matched from . /// /// The type of object the constuctor is expected to yield. /// The unbound generic base type. /// The types of the generic parameters to apply to create a bound generic type. /// The list of values to supply to the constructor. It must be possible to determine the /// The instance created by calling that constructor. /// An instance of type . public T InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, params object[] parameterValues) { Assert.NotNull(genericBaseType); Assert.True(genericBaseType.IsGenericTypeDefinition); Assert.NotNull(genericParameterTypes); Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); ConstructorInfo ctor = FindConstructor(genericType, parameterValues); Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); return (T)ctor.Invoke(parameterValues); } /// /// Asserts the given instance is one from a generic type of the specified parameter type. /// /// The type of instance. /// The instance to test. /// The type of the generic parameter to which the instance's generic type should have been bound. public void IsCorrectGenericType(T instance, Type genericTypeParameter) where T : class { Assert.NotNull(instance); Assert.NotNull(genericTypeParameter); Assert.True(instance.GetType().IsGenericType); Type[] genericArguments = instance.GetType().GetGenericArguments(); Type genericArgument = Assert.Single(genericArguments); Assert.Equal(genericTypeParameter, genericArgument); } /// /// Invokes via Reflection the method on the given instance. /// /// The instance to use. /// The name of the method to call. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public object InvokeMethod(object instance, string methodName, Type[] parameterTypes, object[] parameterValues) { Assert.NotNull(instance); Assert.NotNull(parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); MethodInfo methodInfo = instance.GetType().GetMethod(methodName, parameterTypes); Assert.NotNull(methodInfo); return methodInfo.Invoke(instance, parameterValues); } /// /// Invokes via Reflection the static method on the given type. /// /// The type containing the method. /// The name of the method to call. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public object InvokeMethod(Type type, string methodName, Type[] parameterTypes, object[] parameterValues) { Assert.NotNull(type); Assert.NotNull(parameterTypes); Assert.NotNull(parameterValues); Assert.Equal(parameterTypes.Length, parameterValues.Length); MethodInfo methodInfo = type.GetMethod(methodName, parameterTypes); Assert.NotNull(methodInfo); return methodInfo.Invoke(null, parameterValues); } /// /// Invokes via Reflection the static method on the given type. /// /// The type containing the method. /// The name of the method to call. /// The generic parameter type of the method. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public MethodInfo CreateGenericMethod(Type type, string methodName, Type genericParameterType, Type[] parameterTypes) { Assert.NotNull(type); Assert.NotNull(parameterTypes); Assert.NotNull(genericParameterType); //MethodInfo methodInfo = type.GetMethod(methodName, parameterTypes); MethodInfo methodInfo = type.GetMethods().Where((m) => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase) && m.IsGenericMethod && AreAssignableFrom(m.GetParameters(), parameterTypes)).FirstOrDefault(); Assert.NotNull(methodInfo); Assert.True(methodInfo.IsGenericMethod); MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); Assert.NotNull(genericMethod); return genericMethod; } /// /// Invokes via Reflection the static generic method on the given type. /// /// The type containing the method. /// The name of the method to call. /// The generic parameter type of the method. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public object InvokeGenericMethod(Type type, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) { MethodInfo methodInfo = CreateGenericMethod(type, methodName, genericParameterType, parameterTypes); Assert.Equal(parameterTypes.Length, parameterValues.Length); return methodInfo.Invoke(null, parameterValues); } /// /// Invokes via Reflection the generic method on the given instance. /// /// The instance on which to invoke the method. /// The name of the method to call. /// The generic parameter type of the method. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public object InvokeGenericMethod(object instance, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) { Assert.NotNull(instance); MethodInfo methodInfo = CreateGenericMethod(instance.GetType(), methodName, genericParameterType, parameterTypes); Assert.Equal(parameterTypes.Length, parameterValues.Length); return methodInfo.Invoke(instance, parameterValues); } /// /// Invokes via Reflection the generic method on the given instance. /// /// The type of the return value from the method. /// The instance on which to invoke the method. /// The name of the method to call. /// The generic parameter type of the method. /// The types of the parameters to the method. /// The values to supply to the method. /// The results of the method. public T InvokeGenericMethod(object instance, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) { return (T)InvokeGenericMethod(instance, methodName, genericParameterType, parameterTypes, parameterValues); } /// /// Invokes via Reflection the method on the given instance. /// /// The instance to use. /// The name of the method to call. /// The values to supply to the method. /// The results of the method. public object InvokeMethod(object instance, string methodName, params object[] parameterValues) { Assert.NotNull(instance); MethodInfo methodInfo = FindMethod(instance.GetType(), methodName, parameterValues); Assert.NotNull(methodInfo); return methodInfo.Invoke(instance, parameterValues); } /// /// Invokes via Reflection the static method on the given type. /// /// The instance to use. /// The name of the method to call. /// The values to supply to the method. /// The results of the method. public object InvokeMethod(Type type, string methodName, params object[] parameterValues) { Assert.NotNull(type); MethodInfo methodInfo = FindMethod(type, methodName, parameterValues); Assert.NotNull(methodInfo); return methodInfo.Invoke(null, parameterValues); } /// /// Invokes via Reflection the method on the given instance. /// /// The instance to use. /// The name of the method to call. /// The type of the generic parameter. /// The values to supply to the method. /// The results of the method. public object InvokeGenericMethod(object instance, string methodName, Type genericParameterType, params object[] parameterValues) { Assert.NotNull(instance); Assert.NotNull(genericParameterType); MethodInfo methodInfo = FindMethod(instance.GetType(), methodName, parameterValues); Assert.NotNull(methodInfo); Assert.True(methodInfo.IsGenericMethod); MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); return genericMethod.Invoke(instance, parameterValues); } /// /// Invokes via Reflection the method on the given instance. /// /// The instance to use. /// The name of the method to call. /// The type of the generic parameter. /// The values to supply to the method. /// The results of the method. public object InvokeGenericMethod(Type type, string methodName, Type genericParameterType, params object[] parameterValues) { Assert.NotNull(type); Assert.NotNull(genericParameterType); MethodInfo methodInfo = FindMethod(type, methodName, parameterValues); Assert.NotNull(methodInfo); Assert.True(methodInfo.IsGenericMethod); MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); return genericMethod.Invoke(null, parameterValues); } /// /// Retrieves the value from the specified property. /// /// The instance containing the property value. /// The name of the property. /// The error message to prefix any test assertions. /// The value returned from the property. public object GetProperty(object instance, string propertyName, string failureMessage) { PropertyInfo propertyInfo = instance.GetType().GetProperty(propertyName); Assert.NotNull(propertyInfo); return propertyInfo.GetValue(instance, null); } private static bool AreAssignableFrom(Type[] parameterTypes, params object[] parameterValues) { Assert.NotNull(parameterTypes); Assert.NotNull(parameterValues); if (parameterTypes.Length != parameterValues.Length) { return false; } for (int i = 0; i < parameterTypes.Length; ++i) { if (!parameterTypes[i].IsInstanceOfType(parameterValues[i])) { return false; } } return true; } private static bool AreAssignableFrom(ParameterInfo[] parameterInfos, params Type[] parameterTypes) { Assert.NotNull(parameterInfos); Assert.NotNull(parameterTypes); Type[] parameterInfoTypes = parameterInfos.Select((info) => info.ParameterType).ToArray(); if (parameterInfoTypes.Length != parameterTypes.Length) { return false; } for (int i = 0; i < parameterInfoTypes.Length; ++i) { // Generic parameters are assumed to be assignable if (parameterInfoTypes[i].IsGenericParameter) { continue; } if (!parameterInfoTypes[i].IsAssignableFrom(parameterTypes[i])) { return false; } } return true; } private static bool AreAssignableFrom(ParameterInfo[] parameterInfos, params object[] parameterValues) { Assert.NotNull(parameterInfos); Assert.NotNull(parameterValues); Type[] parameterTypes = parameterInfos.Select((info) => info.ParameterType).ToArray(); return AreAssignableFrom(parameterTypes, parameterValues); } private static ConstructorInfo FindConstructor(Type type, params object[] parameterValues) { Assert.NotNull(type); Assert.NotNull(parameterValues); return type.GetConstructors().FirstOrDefault((c) => AreAssignableFrom(c.GetParameters(), parameterValues)); } private static MethodInfo FindMethod(Type type, string methodName, params object[] parameterValues) { Assert.NotNull(type); Assert.False(String.IsNullOrWhiteSpace(methodName)); Assert.NotNull(parameterValues); return type.GetMethods().FirstOrDefault((m) => String.Equals(m.Name, methodName, StringComparison.Ordinal) && AreAssignableFrom(m.GetParameters(), parameterValues)); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/HttpAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Microsoft.TestCommon { /// /// Unit test utility for testing instances. /// public class HttpAssert { private const string CommaSeperator = ", "; private static readonly HttpAssert singleton = new HttpAssert(); public static HttpAssert Singleton { get { return singleton; } } /// /// Asserts that the expected is equal to the actual . /// /// The expected . Should not be null. /// The actual . Should not be null. public async Task EqualAsync(HttpRequestMessage expected, HttpRequestMessage actual) { Assert.NotNull(expected); Assert.NotNull(actual); Assert.Equal(expected.Version, actual.Version); Equal(expected.Headers, actual.Headers); if (expected.Content == null) { Assert.Null(actual.Content); } else { string expectedContent = CleanContentString(await expected.Content.ReadAsStringAsync()); string actualContent = CleanContentString(await actual.Content.ReadAsStringAsync()); Assert.Equal(expectedContent, actualContent); Equal(expected.Content.Headers, actual.Content.Headers); } } /// /// Asserts that the expected is equal to the actual . /// /// The expected . Should not be null. /// The actual . Should not be null. public Task EqualAsync(HttpResponseMessage expected, HttpResponseMessage actual) { return EqualAsync(expected, actual, verifyContentStringCallback: null); } /// /// Asserts that the expected is equal to the actual . /// /// The expected . Should not be null. /// The actual . Should not be null. /// The callback to verify the Content string. If it is null, Assert.Equal will be used. public async Task EqualAsync(HttpResponseMessage expected, HttpResponseMessage actual, Action verifyContentStringCallback) { Assert.NotNull(expected); Assert.NotNull(actual); Assert.Equal(expected.StatusCode, actual.StatusCode); Assert.Equal(expected.ReasonPhrase, actual.ReasonPhrase); Assert.Equal(expected.Version, actual.Version); Equal(expected.Headers, actual.Headers); if (expected.Content == null) { Assert.Null(actual.Content); } else { string expectedContent = CleanContentString(await expected.Content.ReadAsStringAsync()); string actualContent = CleanContentString(await actual.Content.ReadAsStringAsync()); if (verifyContentStringCallback != null) { verifyContentStringCallback(expectedContent, actualContent); } else { Assert.Equal(expectedContent, actualContent); } Equal(expected.Content.Headers, actual.Content.Headers); } } /// /// Asserts that the expected instance is equal to the actual instance. /// /// The expected instance. Should not be null. /// The actual instance. Should not be null. public void Equal(HttpHeaders expectedHeaders, HttpHeaders actualHeaders) { Assert.NotNull(expectedHeaders); Assert.NotNull(actualHeaders); Assert.Equal(expectedHeaders.Count(), actualHeaders.Count()); foreach (KeyValuePair> expectedHeader in expectedHeaders) { KeyValuePair> actualHeader = actualHeaders.FirstOrDefault(h => h.Key == expectedHeader.Key); if (expectedHeader.Key == "Date") { HandleDateHeader(expectedHeader.Value.ToArray(), actualHeader.Value.ToArray()); } else { string expectedHeaderStr = string.Join(CommaSeperator, expectedHeader.Value); string actualHeaderStr = string.Join(CommaSeperator, actualHeader.Value); Assert.Equal(expectedHeaderStr, actualHeaderStr); } } } /// /// Asserts the given contain the given /// for the given . /// /// The to examine. It cannot be null. /// The name of the header. It cannot be empty. /// The values that must all be present. It cannot be null. public void Contains(HttpHeaders headers, string name, params string[] values) { Assert.NotNull(headers); Assert.False(String.IsNullOrWhiteSpace(name), "Test error: name cannot be empty."); Assert.NotNull(values); IEnumerable headerValues = null; bool foundIt = headers.TryGetValues(name, out headerValues); Assert.True(foundIt); foreach (string value in values) { Assert.Contains(value, headerValues); } } public bool IsKnownUnserializableType(Type type, Func isTypeUnserializableCallback) { if (isTypeUnserializableCallback != null && isTypeUnserializableCallback(type)) { return true; } if (type.IsGenericType) { if (typeof(IEnumerable).IsAssignableFrom(type)) { if (type.GetMethod("Add") == null) { return true; } } // Generic type -- recursively analyze generic arguments return IsKnownUnserializableType(type.GetGenericArguments()[0], isTypeUnserializableCallback); } if (type.HasElementType && IsKnownUnserializableType(type.GetElementType(), isTypeUnserializableCallback)) { return true; } return false; } public bool IsKnownUnserializable(Type type, object obj, Func isTypeUnserializableCallback) { if (IsKnownUnserializableType(type, isTypeUnserializableCallback)) { return true; } return obj != null && IsKnownUnserializableType(obj.GetType(), isTypeUnserializableCallback); } public bool IsKnownUnserializable(Type type, object obj) { return IsKnownUnserializable(type, obj, null); } public bool CanRoundTrip(Type type) { if (typeof(DateTime).IsAssignableFrom(type)) { return false; } if (typeof(DateTimeOffset).IsAssignableFrom(type)) { return false; } if (type.IsGenericType) { foreach (Type genericParameterType in type.GetGenericArguments()) { if (!CanRoundTrip(genericParameterType)) { return false; } } } if (type.HasElementType) { return CanRoundTrip(type.GetElementType()); } return true; } private static void HandleDateHeader(string[] expectedDateHeaderValues, string[] actualDateHeaderValues) { Assert.Equal(expectedDateHeaderValues.Length, actualDateHeaderValues.Length); for (int i = 0; i < expectedDateHeaderValues.Length; i++) { DateTime expectedDateTime = DateTime.Parse(expectedDateHeaderValues[i]); DateTime actualDateTime = DateTime.Parse(actualDateHeaderValues[i]); Assert.Equal(expectedDateTime.Year, actualDateTime.Year); Assert.Equal(expectedDateTime.Month, actualDateTime.Month); Assert.Equal(expectedDateTime.Day, actualDateTime.Day); int hourDifference = Math.Abs(actualDateTime.Hour - expectedDateTime.Hour); Assert.True(hourDifference <= 1); int minuteDifference = Math.Abs(actualDateTime.Minute - expectedDateTime.Minute); Assert.True(minuteDifference <= 1); } } private static string CleanContentString(string content) { Assert.Null(content); string cleanedContent = null; // remove any port numbers from Uri's cleanedContent = Regex.Replace(content, ":\\d+", ""); return cleanedContent; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net.Http.Headers; namespace Microsoft.TestCommon { public class MediaTypeAssert { private static readonly MediaTypeAssert singleton = new MediaTypeAssert(); public static MediaTypeAssert Singleton { get { return singleton; } } public void AreEqual(MediaTypeHeaderValue expected, MediaTypeHeaderValue actual, string errorMessage) { if (expected != null || actual != null) { Assert.NotNull(expected); Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actual)); } } public void AreEqual(MediaTypeHeaderValue expected, string actual, string errorMessage) { if (expected != null || !String.IsNullOrEmpty(actual)) { MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual); Assert.NotNull(expected); Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actualMediaType)); } } public void AreEqual(string expected, string actual, string errorMessage) { if (!String.IsNullOrEmpty(expected) || !String.IsNullOrEmpty(actual)) { Assert.NotNull(expected); MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected); MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual); Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actualMediaType)); } } public void AreEqual(string expected, MediaTypeHeaderValue actual, string errorMessage) { if (!String.IsNullOrEmpty(expected) || actual != null) { Assert.NotNull(expected); MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected); Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actual)); } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeHeaderValueComparer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Net.Http.Headers; namespace Microsoft.TestCommon { public class MediaTypeHeaderValueComparer : IComparer { private static readonly MediaTypeHeaderValueComparer mediaTypeComparer = new MediaTypeHeaderValueComparer(); public MediaTypeHeaderValueComparer() { } public static MediaTypeHeaderValueComparer Comparer { get { return mediaTypeComparer; } } public int Compare(MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2) { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2); int returnValue = CompareBasedOnQualityFactor(parsedMediaType1, parsedMediaType2); if (returnValue == 0) { if (!String.Equals(parsedMediaType1.Type, parsedMediaType2.Type, StringComparison.OrdinalIgnoreCase)) { if (parsedMediaType1.IsAllMediaRange) { return 1; } else if (parsedMediaType2.IsAllMediaRange) { return -1; } } else if (!String.Equals(parsedMediaType1.SubType, parsedMediaType2.SubType, StringComparison.OrdinalIgnoreCase)) { if (parsedMediaType1.IsSubTypeMediaRange) { return 1; } else if (parsedMediaType2.IsSubTypeMediaRange) { return -1; } } else { if (!parsedMediaType1.HasNonQualityFactorParameter) { if (parsedMediaType2.HasNonQualityFactorParameter) { return 1; } } else if (!parsedMediaType2.HasNonQualityFactorParameter) { return -1; } } } return returnValue; } private static int CompareBasedOnQualityFactor(ParsedMediaTypeHeaderValue parsedMediaType1, ParsedMediaTypeHeaderValue parsedMediaType2) { double qualityDifference = parsedMediaType1.QualityFactor - parsedMediaType2.QualityFactor; if (qualityDifference < 0) { return 1; } else if (qualityDifference > 0) { return -1; } return 0; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/ParsedMediaTypeHeaderValue.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net.Http.Headers; namespace Microsoft.TestCommon { internal class ParsedMediaTypeHeaderValue { private const string MediaRangeAsterisk = "*"; private const char MediaTypeSubTypeDelimiter = '/'; private const string QualityFactorParameterName = "q"; private const double DefaultQualityFactor = 1.0; private MediaTypeHeaderValue mediaType; private string type; private string subType; private bool? hasNonQualityFactorParameter; private double? qualityFactor; public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaType) { this.mediaType = mediaType; string[] splitMediaType = mediaType.MediaType.Split(MediaTypeSubTypeDelimiter); this.type = splitMediaType[0]; this.subType = splitMediaType[1]; } public string Type { get { return this.type; } } public string SubType { get { return this.subType; } } public bool IsAllMediaRange { get { return this.IsSubTypeMediaRange && String.Equals(MediaRangeAsterisk, this.Type, StringComparison.Ordinal); } } public bool IsSubTypeMediaRange { get { return String.Equals(MediaRangeAsterisk, this.SubType, StringComparison.Ordinal); } } public bool HasNonQualityFactorParameter { get { if (!this.hasNonQualityFactorParameter.HasValue) { this.hasNonQualityFactorParameter = false; foreach (NameValueHeaderValue param in this.mediaType.Parameters) { if (!String.Equals(QualityFactorParameterName, param.Name, StringComparison.Ordinal)) { this.hasNonQualityFactorParameter = true; } } } return this.hasNonQualityFactorParameter.Value; } } public string CharSet { get { return this.mediaType.CharSet; } } public double QualityFactor { get { if (!this.qualityFactor.HasValue) { MediaTypeWithQualityHeaderValue mediaTypeWithQuality = this.mediaType as MediaTypeWithQualityHeaderValue; if (mediaTypeWithQuality != null) { this.qualityFactor = mediaTypeWithQuality.Quality; } if (!this.qualityFactor.HasValue) { this.qualityFactor = DefaultQualityFactor; } } return this.qualityFactor.Value; } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/RegexReplacement.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Text.RegularExpressions; namespace Microsoft.TestCommon { public class RegexReplacement { Regex regex; string replacement; public RegexReplacement(Regex regex, string replacement) { this.regex = regex; this.replacement = replacement; } public RegexReplacement(string regex, string replacement) { this.regex = new Regex(regex); this.replacement = replacement; } public Regex Regex { get { return this.regex; } } public string Replacement { get { return this.replacement; } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/RuntimeEnvironment.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.Win32; namespace Microsoft.TestCommon { public static class RuntimeEnvironment { private const string NetFx40FullSubKey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"; private const string Version = "Version"; static RuntimeEnvironment() { object runtimeVersion = Registry.LocalMachine.OpenSubKey(RuntimeEnvironment.NetFx40FullSubKey).GetValue(RuntimeEnvironment.Version); string versionFor40String = runtimeVersion as string; if (versionFor40String != null) { VersionFor40 = new Version(versionFor40String); } } private static Version VersionFor40; public static bool IsVersion45Installed { get { return VersionFor40.Major > 4 || (VersionFor40.Major == 4 && VersionFor40.Minor >= 5); } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/SerializerAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Xml.Serialization; namespace Microsoft.TestCommon { /// /// MSTest utility for testing code operating against a stream. /// public class SerializerAssert { private static SerializerAssert singleton = new SerializerAssert(); public static SerializerAssert Singleton { get { return singleton; } } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. It cannot be null. /// The value to serialize. /// Code to check the contents of the stream. public void UsingXmlSerializer(Type type, object objectInstance, Action codeThatChecks) { if (type == null) { throw new ArgumentNullException("type"); } if (codeThatChecks == null) { throw new ArgumentNullException("codeThatChecks"); } XmlSerializer serializer = new XmlSerializer(type); using (MemoryStream stream = new MemoryStream()) { serializer.Serialize(stream, objectInstance); stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); codeThatChecks(stream); } } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. /// The value to serialize. /// Code to check the contents of the stream. public void UsingXmlSerializer(T objectInstance, Action codeThatChecks) { UsingXmlSerializer(typeof(T), objectInstance, codeThatChecks); } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. It cannot be null. /// The value to serialize. /// Code to check the contents of the stream. public void UsingDataContractSerializer(Type type, object objectInstance, Action codeThatChecks) { if (type == null) { throw new ArgumentNullException("type"); } if (codeThatChecks == null) { throw new ArgumentNullException("codeThatChecks"); } DataContractSerializer serializer = new DataContractSerializer(type); using (MemoryStream stream = new MemoryStream()) { serializer.WriteObject(stream, objectInstance); stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); codeThatChecks(stream); } } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. /// The value to serialize. /// Code to check the contents of the stream. public void UsingDataContractSerializer(T objectInstance, Action codeThatChecks) { UsingDataContractSerializer(typeof(T), objectInstance, codeThatChecks); } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. It cannot be null. /// The value to serialize. /// Code to check the contents of the stream. public static void UsingDataContractJsonSerializer(Type type, object objectInstance, Action codeThatChecks) { if (type == null) { throw new ArgumentNullException("type"); } if (codeThatChecks == null) { throw new ArgumentNullException("codeThatChecks"); } DataContractJsonSerializer serializer = new DataContractJsonSerializer(type); using (MemoryStream stream = new MemoryStream()) { serializer.WriteObject(stream, objectInstance); stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); codeThatChecks(stream); } } /// /// Creates a , serializes to it using /// , rewinds the stream and calls . /// /// The type to serialize. /// The value to serialize. /// Code to check the contents of the stream. public void UsingDataContractJsonSerializer(T objectInstance, Action codeThatChecks) { UsingDataContractJsonSerializer(typeof(T), objectInstance, codeThatChecks); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/StreamAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Threading.Tasks; namespace Microsoft.TestCommon { //// TODO RONCAIN using System.Runtime.Serialization.Json; /// /// MSTest utility for testing code operating against a stream. /// public class StreamAssert { private static StreamAssert singleton = new StreamAssert(); public static StreamAssert Singleton { get { return singleton; } } /// /// Creates a , invokes to write to it, /// rewinds the stream to the beginning and invokes . /// /// Code to write to the stream. It cannot be null. /// Code that reads from the stream. It cannot be null. public async Task WriteAndReadAsync(Func codeThatWrites, Func codeThatReadsAsync) { if (codeThatWrites == null) { throw new ArgumentNullException("codeThatWrites"); } if (codeThatReadsAsync == null) { throw new ArgumentNullException("codeThatReads"); } using (MemoryStream stream = new MemoryStream()) { await codeThatWrites(stream); stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); await codeThatReadsAsync(stream); } } /// /// Creates a , invokes to write to it, /// rewinds the stream to the beginning and invokes to obtain /// the result to return from this method. /// /// Code to write to the stream. It cannot be null. /// Code that reads from the stream and returns the result. It cannot be null. /// The value returned from . public static object WriteAndReadResult(Action codeThatWrites, Func codeThatReads) { if (codeThatWrites == null) { throw new ArgumentNullException("codeThatWrites"); } if (codeThatReads == null) { throw new ArgumentNullException("codeThatReads"); } object result = null; using (MemoryStream stream = new MemoryStream()) { codeThatWrites(stream); stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); result = codeThatReads(stream); } return result; } /// /// Creates a , invokes to write to it, /// rewinds the stream to the beginning and invokes to obtain /// the result to return from this method. /// /// The type of the result expected. /// Code to write to the stream. It cannot be null. /// Code that reads from the stream and returns the result. It cannot be null. /// The value returned from . public T WriteAndReadResult(Action codeThatWrites, Func codeThatReads) { return (T)WriteAndReadResult(codeThatWrites, codeThatReads); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/TaskAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; namespace Microsoft.TestCommon { /// /// MSTest assert class to make assertions about tests using . /// public class TaskAssert { /// /// Asserts the given task has been started. TAP guidelines are that all /// objects returned from public API's have been started. /// /// The to test. public void IsStarted(Task task) { Assert.NotNull(task); Assert.True(task.Status != TaskStatus.Created); } /// /// Asserts the given task completes successfully. This method will block the /// current thread waiting for the task, but will timeout if it does not complete. /// /// The to test. public Task SucceedsAsync(Task task) { IsStarted(task); return task; } /// /// Asserts the given task completes successfully and returns a result. /// This method will block the current thread waiting for the task, but will timeout if it does not complete. /// /// The result of the . /// The to test. /// The result from that task. public Task SucceedsWithResultAsync(Task task) { IsStarted(task); return task; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/TestDataSetAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Microsoft.TestCommon { public class TestDataSetAttribute : DataAttribute { public Type DeclaringType { get; set; } public string PropertyName { get; set; } public TestDataVariations TestDataVariations { get; set; } private IEnumerable> ExtraDataSets { get; set; } public TestDataSetAttribute(Type declaringType, string propertyName, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) { DeclaringType = declaringType; PropertyName = propertyName; TestDataVariations = testDataVariations; ExtraDataSets = new List>(); } public TestDataSetAttribute(Type declaringType, string propertyName, Type declaringType1, string propertyName1, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) : this(declaringType, propertyName, testDataVariations) { ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1) }; } public TestDataSetAttribute(Type declaringType, string propertyName, Type declaringType1, string propertyName1, Type declaringType2, string propertyName2, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) : this(declaringType, propertyName, testDataVariations) { ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2) }; } public TestDataSetAttribute(Type declaringType, string propertyName, Type declaringType1, string propertyName1, Type declaringType2, string propertyName2, Type declaringType3, string propertyName3, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) : this(declaringType, propertyName, testDataVariations) { ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2), Tuple.Create(declaringType3, propertyName3) }; } public TestDataSetAttribute(Type declaringType, string propertyName, Type declaringType1, string propertyName1, Type declaringType2, string propertyName2, Type declaringType3, string propertyName3, Type declaringType4, string propertyName4, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) : this(declaringType, propertyName, testDataVariations) { ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2), Tuple.Create(declaringType3, propertyName3), Tuple.Create(declaringType4, propertyName4) }; } public override IEnumerable GetData(MethodInfo methodUnderTest, Type[] parameterTypes) { IEnumerable baseDataSet = GetBaseDataSet(DeclaringType, PropertyName, TestDataVariations); IEnumerable> extraDataSets = GetExtraDataSets(); IEnumerable> finalDataSets = (new[] { baseDataSet }).Concat(extraDataSets); var datasets = CrossProduct(finalDataSets); return datasets; } private static IEnumerable CrossProduct(IEnumerable> datasets) { if (datasets.Count() == 1) { foreach (var dataset in datasets.First()) { yield return dataset; } } else { IEnumerable datasetLeft = datasets.First(); IEnumerable datasetRight = CrossProduct(datasets.Skip(1)); foreach (var dataLeft in datasetLeft) { foreach (var dataRight in datasetRight) { yield return dataLeft.Concat(dataRight).ToArray(); } } } } // The base data set(first one) can either be a TestDataSet or a TestDataSetCollection private static IEnumerable GetBaseDataSet(Type declaringType, string propertyName, TestDataVariations variations) { return TryGetDataSetFromTestDataCollection(declaringType, propertyName, variations) ?? GetDataSet(declaringType, propertyName); } private IEnumerable> GetExtraDataSets() { foreach (var tuple in ExtraDataSets) { yield return GetDataSet(tuple.Item1, tuple.Item2); } } private static object GetTestDataPropertyValue(Type declaringType, string propertyName) { PropertyInfo property = declaringType.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); if (property == null) { throw new ArgumentException(String.Format("Could not find public static property {0} on {1}", propertyName, declaringType.FullName)); } else { return property.GetValue(null, null); } } private static IEnumerable GetDataSet(Type declaringType, string propertyName) { object propertyValue = GetTestDataPropertyValue(declaringType, propertyName); // box the dataset items if the property is not a RefTypeTestData IEnumerable value = (propertyValue as IEnumerable) ?? (propertyValue as IEnumerable).Cast(); if (value == null) { throw new InvalidOperationException(String.Format("{0}.{1} is either null or does not implement IEnumerable", declaringType.FullName, propertyName)); } IEnumerable dataset = value as IEnumerable; if (dataset != null) { return dataset; } else { return value.Select((data) => new object[] { data }); } } private static IEnumerable TryGetDataSetFromTestDataCollection(Type declaringType, string propertyName, TestDataVariations variations) { object propertyValue = GetTestDataPropertyValue(declaringType, propertyName); IEnumerable testDataCollection = propertyValue as IEnumerable; return testDataCollection == null ? null : GetDataSetFromTestDataCollection(testDataCollection, variations); } private static IEnumerable GetDataSetFromTestDataCollection(IEnumerable testDataCollection, TestDataVariations variations) { foreach (TestData testdataInstance in testDataCollection) { foreach (TestDataVariations variation in testdataInstance.GetSupportedTestDataVariations()) { if ((variation & variations) == variation) { Type variationType = testdataInstance.GetAsTypeOrNull(variation); object testData = testdataInstance.GetAsTestDataOrNull(variation); if (AsSingleInstances(variation)) { foreach (object obj in (IEnumerable)testData) { yield return new object[] { variationType, obj }; } } else { yield return new object[] { variationType, testData }; } } } } } private static bool AsSingleInstances(TestDataVariations variation) { return variation == TestDataVariations.AsInstance || variation == TestDataVariations.AsNullable || variation == TestDataVariations.AsDerivedType || variation == TestDataVariations.AsKnownType || variation == TestDataVariations.AsDataMember || variation == TestDataVariations.AsClassMember || variation == TestDataVariations.AsXmlElementProperty; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/TimeoutConstant.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon { /// /// MSTest timeout constants for use with the . /// public class TimeoutConstant { private const int seconds = 1000; /// /// The default timeout for test methods. /// public const int DefaultTimeout = 30 * seconds; /// /// An extended timeout for longer running test methods. /// public const int ExtendedTimeout = 240 * seconds; } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/TypeAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon { /// /// MSTest utility for testing that a given type has the expected properties such as being public, sealed, etc. /// public class TypeAssert { /// /// Specifies a set of type properties to test for using the method. /// This enumeration has a attribute that allows a bitwise combination of its member values. /// [Flags] public enum TypeProperties { /// /// Indicates that the type must be abstract. /// IsAbstract = 0x1, /// /// Indicates that the type must be a class. /// IsClass = 0x2, /// /// Indicates that the type must be a COM object. /// IsComObject = 0x4, /// /// Indicates that the type must be disposable. /// IsDisposable = 0x8, /// /// Indicates that the type must be an enum. /// IsEnum = 0x10, /// /// Indicates that the type must be a generic type. /// IsGenericType = 0x20, /// /// Indicates that the type must be a generic type definition. /// IsGenericTypeDefinition = 0x40, /// /// Indicates that the type must be an interface. /// IsInterface = 0x80, /// /// Indicates that the type must be nested and declared private. /// IsNestedPrivate = 0x100, /// /// Indicates that the type must be nested and declared public. /// IsNestedPublic = 0x200, /// /// Indicates that the type must be public. /// IsPublic = 0x400, /// /// Indicates that the type must be sealed. /// IsSealed = 0x800, /// /// Indicates that the type must be visible outside the assembly. /// IsVisible = 0x1000, /// /// Indicates that the type must be static. /// IsStatic = TypeAssert.TypeProperties.IsAbstract | TypeAssert.TypeProperties.IsSealed, /// /// Indicates that the type must be a public, visible class. /// IsPublicVisibleClass = TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsPublic | TypeAssert.TypeProperties.IsVisible } private static void CheckProperty(Type type, bool expected, bool actual, string property) { Assert.NotNull(type); Assert.True(expected == actual, String.Format("Type '{0}' should{1} be {2}.", type.FullName, expected ? "" : " NOT", property)); } /// /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. /// The method asserts if one or more of the properties are not satisfied. /// /// The type to test for properties. /// The set of type properties to test for. public void HasProperties(TypeProperties typeProperties) { HasProperties(typeof(T), typeProperties); } /// /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. /// The method asserts if one or more of the properties are not satisfied. /// /// The type to test for properties. /// Verify that the type to test is assignable from this type. /// The set of type properties to test for. public void HasProperties(TypeProperties typeProperties) { HasProperties(typeof(T), typeProperties, typeof(TIsAssignableFrom)); } /// /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. /// The method asserts if one or more of the properties are not satisfied. /// /// The type to test for properties. /// The set of type properties to test for. public void HasProperties(Type type, TypeProperties typeProperties) { HasProperties(type, typeProperties, null); } /// /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. /// The method asserts if one or more of the properties are not satisfied. /// /// The type to test for properties. /// The set of type properties to test for. /// Verify that the type to test is assignable from this type. public void HasProperties(Type type, TypeProperties typeProperties, Type isAssignableFrom) { TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsAbstract) > 0, type.IsAbstract, "abstract"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsClass) > 0, type.IsClass, "a class"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsComObject) > 0, type.IsCOMObject, "a COM object"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsDisposable) > 0, typeof(IDisposable).IsAssignableFrom(type), "disposable"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsEnum) > 0, type.IsEnum, "an enum"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericType) > 0, type.IsGenericType, "a generic type"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericTypeDefinition) > 0, type.IsGenericTypeDefinition, "a generic type definition"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsInterface) > 0, type.IsInterface, "an interface"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPrivate) > 0, type.IsNestedPrivate, "nested private"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPublic) > 0, type.IsNestedPublic, "nested public"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsPublic) > 0, type.IsPublic, "public"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsSealed) > 0, type.IsSealed, "sealed"); TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsVisible) > 0, type.IsVisible, "visible"); if (isAssignableFrom != null) { TypeAssert.CheckProperty(type, true, isAssignableFrom.IsAssignableFrom(type), String.Format("assignable from {0}", isAssignableFrom.FullName)); } } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ByteEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum ByteEnum : byte { FirstByte, SecondByte, ThirdByte } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/FlagsEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon.Types { [Flags] public enum FlagsEnum { One = 0x1, Two = 0x2, Four = 0x4 } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/INameAndIdContainer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { /// /// Tagging interface to assist comparing instances of these types. /// public interface INameAndIdContainer { string Name { get; set; } int Id { get; set; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ISerializableType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Runtime.Serialization; namespace Microsoft.TestCommon.Types { [Serializable] public class ISerializableType : ISerializable, INameAndIdContainer { private int id; private string name; public ISerializableType() { } public ISerializableType(int id, string name) { this.id = id; this.name = name; } public ISerializableType(SerializationInfo information, StreamingContext context) { this.id = information.GetInt32("Id"); this.name = information.GetString("Name"); } public int Id { get { return this.id; } set { this.IdSet = true; this.id = value; } } public string Name { get { return this.name; } set { this.NameSet = true; this.name = value; } } public bool IdSet { get; private set; } public bool NameSet { get; private set; } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Id", this.Id); info.AddValue("Name", this.Name); } public static IEnumerable GetTestData() { return new ISerializableType[] { new ISerializableType(), new ISerializableType(1, "SomeName") }; } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/LongEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum LongEnum : long { FirstLong, SecondLong, ThirdLong, FourthLong } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SByteEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum SByteEnum : sbyte { FirstSByte, SecondSByte, ThirdSByte } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ShortEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum ShortEnum : short { FirstShort, SecondShort, ThirdShort } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SimpleEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum SimpleEnum { First, Second, Third, Fourth } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UIntEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum UIntEnum : uint { FirstUInt, SecondUInt, ThirdUInt } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UShortEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace Microsoft.TestCommon.Types { public enum UShortEnum : ushort { FirstUShort, SecondUShort, ThirdUShort } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft/TestCommon/XmlAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml.Linq; namespace Microsoft.TestCommon { /// /// Assert class that compares two XML strings for equality. Namespaces are ignored during comparison /// public class XmlAssert { public void Equal(string expected, string actual, params RegexReplacement[] regexReplacements) { if (regexReplacements != null) { for (int i = 0; i < regexReplacements.Length; i++) { actual = regexReplacements[i].Regex.Replace(actual, regexReplacements[i].Replacement); } } Equal(XElement.Parse(expected), XElement.Parse(actual)); } public void Equal(XElement expected, XElement actual) { Assert.Equal(Normalize(expected).ToString(), Normalize(actual).ToString()); } private static XElement Normalize(XElement element) { if (element.HasElements) { return new XElement( Encode(element.Name), Normalize(element.Attributes()), Normalize(element.Elements())); } if (element.IsEmpty) { return new XElement( Encode(element.Name), Normalize(element.Attributes())); } else { return new XElement( Encode(element.Name), Normalize(element.Attributes()), element.Value); } } private static IEnumerable Normalize(IEnumerable attributes) { return attributes .Where((attrib) => !attrib.IsNamespaceDeclaration) .Select((attrib) => new XAttribute(Encode(attrib.Name), attrib.Value)) .OrderBy(a => a.Name.ToString()); } private static IEnumerable Normalize(IEnumerable elements) { return elements .Select(e => Normalize(e)) .OrderBy(a => a.ToString()); } private static string Encode(XName name) { return string.Format("{0}_{1}", HttpUtility.UrlEncode(name.NamespaceName).Replace('%', '_'), name.LocalName); } } } ================================================ FILE: test/Microsoft.TestCommon/Microsoft.TestCommon.csproj ================================================  net462;netcoreapp2.1;net8.0 $(Configurations);CodeAnalysis $(DefineConstants);Testing_NetStandard1_3 false ..\..\bin\$(Configuration)\Test\ $(OutputPath)ns1_3\ ================================================ FILE: test/Microsoft.TestCommon/Platform.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon { /// /// An enumeration of known platforms that the unit test might be running under. /// [Flags] public enum Platform { /// /// A special value used to indicate that the test is valid on all known platforms. /// All = 0xFFFFFF, /// /// Indicates that the test wants to run on .NET 4 (when used with /// and/or ), /// or that the current platform that the test is running on is .NET 4 (when used with the /// , , and/or /// ). /// Net40 = 0x01, /// /// Indicates that the test wants to run on .NET 4.5 (when used with /// and/or ), /// or that the current platform that the test is running on is .NET 4.5 (when used with the /// , , and/or /// ). /// Net45 = 0x02, } } ================================================ FILE: test/Microsoft.TestCommon/PlatformInfo.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; namespace Microsoft.TestCommon { /// /// Used to retrieve the currently running platform. /// public static class PlatformInfo { private const string _net45TypeName = "System.IWellKnownStringEqualityComparer, mscorlib, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089"; private const string _netCore20TypeName = "System.OrdinalCaseSensitiveComparer, system.private.corelib, Version=4.0.0.0, PublicKeyToken=7cec85d7bea7798e"; private static Lazy _platform = new Lazy(GetPlatform, isThreadSafe: true); /// /// Gets the platform that the unit test is currently running on. /// public static Platform Platform { get { return _platform.Value; } } private static Platform GetPlatform() { if (Type.GetType(_netCore20TypeName, throwOnError: false) != null) { // Treat .NET Core 2.1 as a .NET 4.5 superset though internal types are different. return Platform.Net45; } if (Type.GetType(_net45TypeName, throwOnError: false) != null) { return Platform.Net45; } return Platform.Net40; } } } ================================================ FILE: test/Microsoft.TestCommon/PortReserver.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading; namespace Microsoft.TestCommon { [Xunit.CollectionDefinition("PortReserver Collection", DisableParallelization = true)] public class PortReserverCollection { } /// /// This class allocates ports while ensuring that: /// 1. Ports that are permanently taken (or taken for the duration of the test) are not being attempted to be used. /// 2. Ports are not shared across different tests (but you can allocate two different ports in the same test). /// /// Gotcha: If another application grabs a port during the test, we have a race condition. /// [DebuggerDisplay("Port: {PortNumber}, Port count for this app domain: {_appDomainOwnedPorts.Count}")] public class PortReserver : IDisposable { private Mutex _portMutex; private Thread _acquiredOn; // We use this list to hold on to all the ports used because the Mutex will be blown through on the same thread. // Theoretically we can do a thread local HashSet, but that makes dispose thread-dependent, or requires more complicated concurrency checks. // Since practically there is no perf issue or concern here, this keeps the code the simplest possible. private static HashSet _appDomainOwnedPorts = new HashSet(); public int PortNumber { get; private set; } public PortReserver(int basePort = 50231) { if (basePort <= 0) { throw new ArgumentOutOfRangeException("basePort", "Argument must be greater than 0."); } // Grab a cross appdomain/cross process/cross thread lock, to ensure only one port is reserved at a time. using (Mutex mutex = GetGlobalMutex()) { try { var usedTCPPorts = new HashSet(); foreach (var endPoint in ListUsedTCPPort()) { usedTCPPorts.Add(endPoint.Port); } int port = basePort - 1; while (true) { port++; if (port > 65535) { throw new InvalidOperationException("Exceeded port range"); } // AppDomainOwnedPorts check enables reserving two ports from the same thread in sequence. // ListUsedTCPPort prevents port contention with other apps. if (_appDomainOwnedPorts.Contains(port) || usedTCPPorts.Contains(port)) { continue; } // Create a well known mutex string mutexName = "WebStack-Port-" + port.ToString(CultureInfo.InvariantCulture); _portMutex = new Mutex(initiallyOwned: false, name: mutexName); // If no one else is using this port grab it. if (_portMutex.WaitOne(millisecondsTimeout: 0)) { _acquiredOn = Thread.CurrentThread; break; } // dispose this mutex since the port it represents is not available. _portMutex.Dispose(); _portMutex = null; } PortNumber = port; _appDomainOwnedPorts.Add(port); } finally { mutex.ReleaseMutex(); } } } public string BaseUri { get { return String.Format(CultureInfo.InvariantCulture, "http://localhost:{0}/", PortNumber); } } public void Dispose() { if (PortNumber == -1) { // Object already disposed return; } using (Mutex mutex = GetGlobalMutex()) { try { using (_portMutex) { if (_acquiredOn == Thread.CurrentThread) { _portMutex.ReleaseMutex(); } _portMutex = null; } _appDomainOwnedPorts.Remove(PortNumber); PortNumber = -1; } finally { mutex.ReleaseMutex(); } } } private static Mutex GetGlobalMutex() { const int timeoutInSeconds = 20; Mutex mutex = new Mutex(initiallyOwned: false, name: "WebStack-RandomPortAcquisition"); if (!mutex.WaitOne(TimeSpan.FromSeconds(timeoutInSeconds))) { throw new InvalidOperationException( String.Format("Unable to reserve global Mutex within {0} seconds.", timeoutInSeconds)); } return mutex; } private static IPEndPoint[] ListUsedTCPPort() { IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); return ipGlobalProperties.GetActiveTcpListeners() .Concat(ipGlobalProperties.GetActiveUdpListeners()) .ToArray(); } } } ================================================ FILE: test/Microsoft.TestCommon/PreAppStartTestHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.ComponentModel; using System.Reflection; using Microsoft.TestCommon; namespace System.Web.WebPages.TestUtils { public static class PreAppStartTestHelper { public static void TestPreAppStartClass(Type preAppStartType) { string typeMessage = String.Format("The type '{0}' must be static, public, and named 'PreApplicationStartCode'.", preAppStartType.FullName); Assert.True(preAppStartType.IsSealed && preAppStartType.IsAbstract && preAppStartType.IsPublic && preAppStartType.Name == "PreApplicationStartCode", typeMessage); string editorBrowsableMessage = String.Format("The only attribute on type '{0}' must be [EditorBrowsable(EditorBrowsableState.Never)].", preAppStartType.FullName); object[] attrs = preAppStartType.GetCustomAttributes(typeof(EditorBrowsableAttribute), true); Assert.True(attrs.Length == 1 && ((EditorBrowsableAttribute)attrs[0]).State == EditorBrowsableState.Never, editorBrowsableMessage); string startMethodMessage = String.Format("The only public member on type '{0}' must be a method called Start().", preAppStartType.FullName); MemberInfo[] publicMembers = preAppStartType.GetMembers(BindingFlags.Public | BindingFlags.Static); Assert.True(publicMembers.Length == 1, startMethodMessage); Assert.True(publicMembers[0].MemberType == MemberTypes.Method, startMethodMessage); Assert.True(publicMembers[0].Name == "Start", startMethodMessage); } } } ================================================ FILE: test/Microsoft.TestCommon/PreserveSyncContextAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; namespace Microsoft.TestCommon { /// /// Preserves the current . Use this attribute on /// tests which modify the current . /// public class PreserveSyncContextAttribute : Xunit.Sdk.BeforeAfterTestAttribute { private SynchronizationContext _syncContext; public override void Before(System.Reflection.MethodInfo methodUnderTest) { _syncContext = SynchronizationContext.Current; } public override void After(System.Reflection.MethodInfo methodUnderTest) { SynchronizationContext.SetSynchronizationContext(_syncContext); } } } ================================================ FILE: test/Microsoft.TestCommon/PropertyDataAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using Xunit; using Xunit.Sdk; namespace Microsoft.TestCommon { // Xunit.MemberDataAttribute is unfortunately sealed. Duplicate its code here since method we need is protected. [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] [DataDiscoverer("Xunit.Sdk.MemberDataDiscoverer", "xunit.core")] public class PropertyDataAttribute : MemberDataAttributeBase { public PropertyDataAttribute(string propertyName, params object[] parameters) : base(propertyName, parameters) { } public Type PropertyType { get { return MemberType; } set { MemberType = value; } } protected override object[] ConvertDataItem(MethodInfo testMethod, object item) { if (item == null) { return null; } var array = item as object[]; if (array == null) { throw new ArgumentException(String.Format( "Property {0} on {1} yielded an item that is not an object[].", MemberName, MemberType ?? testMethod.DeclaringType)); } return array; } } } ================================================ FILE: test/Microsoft.TestCommon/ReflectionAssert.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq.Expressions; using System.Reflection; namespace Microsoft.TestCommon { public class ReflectionAssert { private static PropertyInfo GetPropertyInfo(Expression> property) { if (property.Body is MemberExpression) { return (PropertyInfo)((MemberExpression)property.Body).Member; } else if (property.Body is UnaryExpression && property.Body.NodeType == ExpressionType.Convert) { return (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member; } else { throw new InvalidOperationException("Could not determine property from lambda expression."); } } private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue valueToSet, TValue valueToCheck) { setFunc(instance, valueToSet); TValue newValue = getFunc(instance); Assert.Equal(valueToCheck, newValue); } private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue value) { TestPropertyValue(instance, getFunc, setFunc, value, value); } public void Property(T instance, Expression> propertyGetter, TResult expectedDefaultValue, bool allowNull = false, TResult roundTripTestValue = null) where TResult : class { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (allowNull) { TestPropertyValue(instance, getFunc, setFunc, null); } else { Assert.ThrowsArgumentNull(() => { setFunc(instance, null); }, "value"); } if (roundTripTestValue != null) { TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } } public void IntegerProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult? minLegalValue, TResult? illegalLowerValue, TResult? maxLegalValue, TResult? illegalUpperValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (minLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, minLegalValue.Value); } if (maxLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, maxLegalValue.Value); } if (illegalLowerValue.HasValue) { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", minLegalValue.Value.ToString(), illegalLowerValue.Value); } if (illegalUpperValue.HasValue) { Assert.ThrowsArgumentLessThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", maxLegalValue.Value.ToString(), illegalUpperValue.Value); } TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void NullableIntegerProperty(T instance, Expression> propertyGetter, TResult? expectedDefaultValue, TResult? minLegalValue, TResult? illegalLowerValue, TResult? maxLegalValue, TResult? illegalUpperValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult?)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); TestPropertyValue(instance, getFunc, setFunc, null); if (minLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, minLegalValue.Value); } if (maxLegalValue.HasValue) { TestPropertyValue(instance, getFunc, setFunc, maxLegalValue.Value); } if (illegalLowerValue.HasValue) { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", minLegalValue.Value.ToString(), illegalLowerValue.Value); } if (illegalUpperValue.HasValue) { Assert.ThrowsArgumentLessThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", maxLegalValue.Value.ToString(), illegalUpperValue.Value); } TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void BooleanProperty(T instance, Expression> propertyGetter, bool expectedDefaultValue) { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (bool)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); TestPropertyValue(instance, getFunc, setFunc, !expectedDefaultValue); } public void EnumProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult illegalValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); Assert.ThrowsInvalidEnumArgument(() => { setFunc(instance, illegalValue); }, "value", Convert.ToInt32(illegalValue), typeof(TResult)); TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void EnumPropertyWithoutIllegalValueCheck(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult roundTripTestValue) where TResult : struct { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); } public void StringProperty(T instance, Expression> propertyGetter, string expectedDefaultValue, bool allowNullAndEmpty = true, bool treatNullAsEmpty = true) { PropertyInfo property = GetPropertyInfo(propertyGetter); Func getFunc = (obj) => (string)property.GetValue(obj, index: null); Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); Assert.Equal(expectedDefaultValue, getFunc(instance)); if (allowNullAndEmpty) { // Assert get/set works for null TestPropertyValue(instance, getFunc, setFunc, null, treatNullAsEmpty ? String.Empty : null); // Assert get/set works for String.Empty TestPropertyValue(instance, getFunc, setFunc, String.Empty, String.Empty); } else { Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, getFunc, setFunc, null); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); Assert.ThrowsArgumentNullOrEmpty( delegate() { try { TestPropertyValue(instance, getFunc, setFunc, String.Empty); } catch (TargetInvocationException e) { throw e.InnerException; } }, "value"); } // Assert get/set works for arbitrary value TestPropertyValue(instance, getFunc, setFunc, "TestValue"); } } } ================================================ FILE: test/Microsoft.TestCommon/ReplaceCultureAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Reflection; using System.Threading; namespace Microsoft.TestCommon { /// /// Replaces the current culture and UI culture for the test. /// [AttributeUsage(AttributeTargets.Method)] public class ReplaceCultureAttribute : Xunit.Sdk.BeforeAfterTestAttribute { private CultureInfo _originalCulture; private CultureInfo _originalUICulture; public ReplaceCultureAttribute() { Culture = CultureReplacer.DefaultCultureName; UICulture = CultureReplacer.DefaultUICultureName; } /// /// Sets for the test. Defaults to en-GB. /// /// /// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We /// want to be able to find bugs where we're accidentally relying on the Invariant instead of the /// user's culture. /// public string Culture { get; set; } /// /// Sets for the test. Defaults to en-US. /// public string UICulture { get; set; } public override void Before(MethodInfo methodUnderTest) { _originalCulture = Thread.CurrentThread.CurrentCulture; _originalUICulture = Thread.CurrentThread.CurrentUICulture; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(Culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(UICulture); } public override void After(MethodInfo methodUnderTest) { Thread.CurrentThread.CurrentCulture = _originalCulture; Thread.CurrentThread.CurrentUICulture = _originalUICulture; } } } ================================================ FILE: test/Microsoft.TestCommon/RestoreThreadPrincipalAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using System.Security.Principal; using System.Threading; namespace Microsoft.TestCommon { public class RestoreThreadPrincipalAttribute : Xunit.Sdk.BeforeAfterTestAttribute { private IPrincipal _originalPrincipal; public override void Before(MethodInfo methodUnderTest) { _originalPrincipal = Thread.CurrentPrincipal; AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal); Thread.CurrentPrincipal = null; } public override void After(MethodInfo methodUnderTest) { Thread.CurrentPrincipal = _originalPrincipal; AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal); } } } ================================================ FILE: test/Microsoft.TestCommon/SkippedXunitTestCase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.TestCommon { /// /// An which is unconditionally skipped. /// /// /// Similar to Xunit.Sdk.XunitSkippedDataRowTestCase but with a more general name. Besides, that will not /// exist until xUnit.net 2.2.0 is stable. /// public class SkippedXunitTestCase : XunitTestCase { private readonly String _skipReason; /// /// Instantiates a new instance. /// /// The used to send diagnostic messages. /// Default to use (when not customized). /// The reason that this will be skipped. /// The this test case belongs to. /// The arguments for the test method. public SkippedXunitTestCase( IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, String skipReason, ITestMethod testMethod, object[] testMethodArguments = null) : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) { _skipReason = skipReason; } /// protected override string GetSkipReason(IAttributeInfo factAttribute) { return _skipReason; } } } ================================================ FILE: test/Microsoft.TestCommon/TestFile.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Reflection; using Microsoft.TestCommon; namespace System.Web.WebPages.TestUtils { public class TestFile { public const string ResourceNameFormat = "{0}.TestFiles.{1}"; public string ResourceName { get; set; } public Assembly Assembly { get; set; } public TestFile(string resName, Assembly asm) { ResourceName = resName; Assembly = asm; } public static TestFile Create(string localResourceName) { return new TestFile(String.Format(ResourceNameFormat, Assembly.GetCallingAssembly().GetName().Name, localResourceName), Assembly.GetCallingAssembly()); } public Stream OpenRead() { Stream strm = Assembly.GetManifestResourceStream(ResourceName); if (strm == null) { Assert.True(false, String.Format("Manifest resource: {0} not found", ResourceName)); } return strm; } public byte[] ReadAllBytes() { using (Stream stream = OpenRead()) { byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); return buffer; } } public string ReadAllText() { using (StreamReader reader = new StreamReader(OpenRead())) { // The .Replace() calls normalize line endings, in case you get \n instead of \r\n // since all the unit tests rely on the assumption that the files will have \r\n endings. return reader.ReadToEnd().Replace("\r", "").Replace("\n", "\r\n"); } } /// /// Saves the file to the specified path. /// public void Save(string filePath) { var directory = Path.GetDirectoryName(filePath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } using (Stream outStream = File.Create(filePath)) { using (Stream inStream = OpenRead()) { inStream.CopyTo(outStream); } } } } } ================================================ FILE: test/Microsoft.TestCommon/TestHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Linq; using Microsoft.TestCommon; namespace System.Web.TestUtil { public static class UnitTestHelper { public static bool EnglishBuildAndOS { get { bool englishBuild = String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase); bool englishOS = String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", StringComparison.OrdinalIgnoreCase); return englishBuild && englishOS; } } public static void AssertEqualsIgnoreWhitespace(string expected, string actual) { expected = new String(expected.Where(c => !Char.IsWhiteSpace(c)).ToArray()); actual = new String(actual.Where(c => !Char.IsWhiteSpace(c)).ToArray()); Assert.Equal(expected, actual, StringComparer.OrdinalIgnoreCase); } } } ================================================ FILE: test/Microsoft.TestCommon/TheoryAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit.Sdk; namespace Microsoft.TestCommon { /// /// An override of that provides extended capabilities. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] [XunitTestCaseDiscoverer("Microsoft.TestCommon.TheoryDiscoverer", "Microsoft.TestCommon")] public class TheoryAttribute : Xunit.TheoryAttribute { /// /// Instantiates a new instance of . /// public TheoryAttribute() { Platforms = Platform.All; PlatformJustification = "Unsupported platform (test runs on {0}, current platform is {1})"; } /// /// Gets or set the platforms that the unit test is compatible with. Defaults to /// . /// public Platform Platforms { get; set; } /// /// Gets or sets the platform skipping justification. This message can receive /// the supported platforms as {0}, and the current platform as {1}. /// public string PlatformJustification { get; set; } } } ================================================ FILE: test/Microsoft.TestCommon/TheoryDataSet.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; namespace Microsoft.TestCommon { /// /// Helper class for generating test data for XUnit's -based tests. /// Should be used in combination with . /// /// First parameter type public class TheoryDataSet : TheoryDataSet { public void Add(TParam p) { AddItem(p); } } /// /// Helper class for generating test data for XUnit's -based tests. /// Should be used in combination with . /// /// First parameter type /// Second parameter type public class TheoryDataSet : TheoryDataSet { public void Add(TParam1 p1, TParam2 p2) { AddItem(p1, p2); } } /// /// Helper class for generating test data for XUnit's -based tests. /// Should be used in combination with . /// /// First parameter type /// Second parameter type /// Third parameter type public class TheoryDataSet : TheoryDataSet { public void Add(TParam1 p1, TParam2 p2, TParam3 p3) { AddItem(p1, p2, p3); } } /// /// Helper class for generating test data for XUnit's -based tests. /// Should be used in combination with . /// /// First parameter type /// Second parameter type /// Third parameter type /// Fourth parameter type public class TheoryDataSet : TheoryDataSet { public void Add(TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4) { AddItem(p1, p2, p3, p4); } } /// /// Helper class for generating test data for XUnit's -based tests. /// Should be used in combination with . /// /// First parameter type /// Second parameter type /// Third parameter type /// Fourth parameter type /// Fifth parameter type public class TheoryDataSet : TheoryDataSet { public void Add(TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5) { AddItem(p1, p2, p3, p4, p5); } } /// /// Base class for TheoryDataSet classes. /// public abstract class TheoryDataSet : IEnumerable { private readonly List data = new List(); protected void AddItem(params object[] values) { data.Add(values); } public IEnumerator GetEnumerator() { return data.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: test/Microsoft.TestCommon/TheoryDiscoverer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Xunit.Abstractions; using Xunit.Sdk; namespace Microsoft.TestCommon { /// /// An override of that provides extended capabilities. /// public class TheoryDiscoverer : Xunit.Sdk.TheoryDiscoverer { private readonly IMessageSink _diagnosticMessageSink; /// /// Instantiates a new instance. /// /// The used to send diagnostic messages. public TheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { _diagnosticMessageSink = diagnosticMessageSink; } /// /// Gets the platform that the unit test is currently running on. /// protected Platform Platform { get { return PlatformInfo.Platform; } } /// public override IEnumerable Discover( ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) { var baseCases = base.Discover(discoveryOptions, testMethod, theoryAttribute); if (!String.IsNullOrEmpty(theoryAttribute.GetNamedArgument("Skip"))) { // No need to change skipped tests. return baseCases; } var platforms = theoryAttribute.GetNamedArgument("Platforms"); if ((platforms & Platform) != 0) { // No need to change tests that should run on the current platform. return baseCases; } // Update the individual test cases as needed: Skip test cases that would otherwise run. var testCases = new List(); var platformJustification = theoryAttribute.GetNamedArgument("PlatformJustification"); var skipReason = String.Format(platformJustification, platforms.ToString().Replace(", ", " | "), Platform); foreach (var baseCase in baseCases) { if (baseCase is ExecutionErrorTestCase) { // No need to change an erroneous test. Covered to protect against changes in the base class. testCases.Add(baseCase); continue; } if (!String.IsNullOrEmpty(baseCase.SkipReason)) { // No need to change a skipped test. Likely to hit this case only after xUnit.net has been updated // to 2.2.0+, where [Data] also has a Skip property. testCases.Add(baseCase); continue; } var testCase = new SkippedXunitTestCase( _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, skipReason, baseCase.TestMethod, baseCase.TestMethodArguments); testCases.Add(testCase); } return testCases; } } } ================================================ FILE: test/Microsoft.TestCommon/ThreadPoolSyncContext.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; namespace Microsoft.TestCommon { /// /// This is an implementation of SynchronizationContext that not only queues things on the thread pool for /// later work, but also ensures that it sets itself back as the synchronization context (something that the /// default implementatation of SynchronizationContext does not do). /// public class ThreadPoolSyncContext : SynchronizationContext { public override void Post(SendOrPostCallback d, object state) { ThreadPool.QueueUserWorkItem(_ => { SynchronizationContext oldContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(this); d.Invoke(state); SynchronizationContext.SetSynchronizationContext(oldContext); }, null); } public override void Send(SendOrPostCallback d, object state) { SynchronizationContext oldContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(this); d.Invoke(state); SynchronizationContext.SetSynchronizationContext(oldContext); } } } ================================================ FILE: test/Microsoft.TestCommon/TraitAttribute.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Xunit.Sdk; namespace Microsoft.TestCommon { // Xunit.MemberDataAttribute is unfortunately sealed. Duplicate its code here since there's nothing to it. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] [TraitDiscoverer("Xunit.Sdk.TraitDiscoverer", "xunit.core")] public class TraitAttribute : Attribute, ITraitAttribute { public TraitAttribute(string name, string value) { } } } ================================================ FILE: test/Microsoft.TestCommon/VersionTestHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; namespace Microsoft.TestCommon { public static class VersionTestHelper { // returns a version for an assembly by using a type from the assembly // also verifies that type wasn't moved to another assembly. public static Version GetVersionFromAssembly(string assemblyName, Type typeFromAssembly) { Assembly assembly = typeFromAssembly.Assembly; Assert.Equal(assemblyName, assembly.GetName().Name); return assembly.GetName().Version; } } } ================================================ FILE: test/Microsoft.TestCommon/WebUtils.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Reflection; using System.Web.UI; namespace System.Web.WebPages.TestUtils { public static class WebUtils { /// /// Creates an instance of HttpRuntime and assigns it (using magic) to the singleton instance of HttpRuntime. /// Ensure that the returned value is disposed at the end of the test. /// /// Returns an IDisposable that restores the original HttpRuntime. public static IDisposable CreateHttpRuntime(string appVPath, string appPath = null) { var runtime = new HttpRuntime(); var appDomainAppVPathField = typeof(HttpRuntime).GetField("_appDomainAppVPath", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); appDomainAppVPathField.SetValue(runtime, CreateVirtualPath(appVPath)); if (appPath != null) { var appDomainAppPathField = typeof(HttpRuntime).GetField("_appDomainAppPath", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); appDomainAppPathField.SetValue(runtime, Path.GetFullPath(appPath)); } GetTheRuntime().SetValue(null, runtime); var appDomainIdField = typeof(HttpRuntime).GetField("_appDomainId", BindingFlags.NonPublic | BindingFlags.Instance); appDomainIdField.SetValue(runtime, "test"); return new DisposableAction(RestoreHttpRuntime); } internal static FieldInfo GetTheRuntime() { return typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static); } internal static void RestoreHttpRuntime() { GetTheRuntime().SetValue(null, null); } internal static object CreateVirtualPath(string path) { var vPath = typeof(Page).Assembly.GetType("System.Web.VirtualPath"); var method = vPath.GetMethod("CreateNonRelativeTrailingSlash", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return method.Invoke(null, new object[] { path }); } private class DisposableAction : IDisposable { private Action _action; private bool _hasDisposed; public DisposableAction(Action action) { if (action == null) { throw new ArgumentNullException("action"); } _action = action; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // If we were disposed by the finalizer it's because the user didn't use a "using" block, so don't do anything! if (disposing) { lock (this) { if (!_hasDisposed) { _hasDisposed = true; _action(); } } } } } } } ================================================ FILE: test/Microsoft.TestCommon/xunit.runner.json ================================================ { "diagnosticMessages": false, "preEnumerateTheories": false } ================================================ FILE: test/Microsoft.Web.Helpers.Test/AnalyticsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.TestUtil; using Microsoft.TestCommon; namespace Microsoft.Web.Helpers.Test { public class AnalyticsTest { [Fact] public void GetYahooAnalyticsHtmlTest() { string account = "My_yahoo_account"; string actual = Analytics.GetYahooHtml(account).ToString(); Assert.True(actual.Contains(".yahoo.com") && actual.Contains("My_yahoo_account")); } [Fact] public void GetStatCounterAnalyticsHtmlTest() { int project = 31553; string security = "stat_security"; string actual = Analytics.GetStatCounterHtml(project, security).ToString(); Assert.True(actual.Contains("statcounter.com/counter/counter_xhtml.js") && actual.Contains(project.ToString()) && actual.Contains(security)); } [Fact] public void GetGoogleAnalyticsHtmlTest() { string account = "My_google_account"; string actual = Analytics.GetGoogleHtml(account).ToString(); Assert.True(actual.Contains("google-analytics.com/ga.js") && actual.Contains("My_google_account")); } [Fact] public void GetGoogleAnalyticsEscapesJavascript() { string account = "My_\"google_account"; string actual = Analytics.GetGoogleHtml(account).ToString(); string expected = "\n" + "\n"; UnitTestHelper.AssertEqualsIgnoreWhitespace(expected, actual); } [Fact] public void GetGoogleAnalyticsAsyncHtmlTest() { string account = "My_google_account"; string actual = Analytics.GetGoogleAsyncHtml(account).ToString(); Assert.True(actual.Contains("google-analytics.com/ga.js") && actual.Contains("My_google_account")); } [Fact] public void GetGoogleAnalyticsAsyncHtmlEscapesJavaScript() { string account = "My_\"google_account"; string actual = Analytics.GetGoogleAsyncHtml(account).ToString(); string expected = "\n"; UnitTestHelper.AssertEqualsIgnoreWhitespace(expected, actual); } [Fact] public void GetYahooAnalyticsEscapesJavascript() { string account = "My_\"yahoo_account"; string actual = Analytics.GetYahooHtml(account).ToString(); string expected = "\n"; UnitTestHelper.AssertEqualsIgnoreWhitespace(expected, actual); } [Fact] public void GetStatCounterAnalyticsEscapesCorrectly() { string account = "My_\"stat_account"; string actual = Analytics.GetStatCounterHtml(2, account).ToString(); string expected = "\n\n" + ""; UnitTestHelper.AssertEqualsIgnoreWhitespace(expected, actual); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/FacebookTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web; using System.Web.TestUtil; using System.Web.WebPages.Scope; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { [Xunit.Collection("Uses ScopeStorage or ViewEngines.Engines")] public class FacebookTest : IDisposable { public FacebookTest() { Facebook.AppId = "myapp'"; Facebook.AppSecret = "myappsecret"; Facebook.Language = "french"; } [Fact] public void GetFacebookCookieInfoReturnsEmptyStringIfCookieIsNotPresent() { // Arrange var context = CreateHttpContext(); // Act var info = Facebook.GetFacebookCookieInfo(context, "foo"); // Assert Assert.Equal("", info); } [Fact] public void GetFacebookCookieInfoThrowsIfCookieIsNotValid() { // Arrange var context = CreateHttpContext(new Dictionary { {"fbs_myapp'", "sig=malformed-signature&name=foo&val=bar&uid=MyFacebookName"}, {"fbs_uid", "MyFacebookName"}, }); // Act and Assert Assert.Throws(() => Facebook.GetFacebookCookieInfo(context, "uid"), "Invalid Facebook cookie."); } [Fact] public void GetFacebookCookieReturnsUserIdIfCookieIsValid() { // Arrange var context = CreateHttpContext(new Dictionary { {"fbs_myapp'", "sig=B2E6B3A21D0C9FA72E612BD6C3084807&name=foo&val=bar&uid=MyFacebookName"}, }); // Act var info = Facebook.GetFacebookCookieInfo(context, "uid"); // Assert Assert.Equal("MyFacebookName", info); } [Fact] public void GetInitScriptsJSEncodesParameters() { // Arrange var expectedText = @"
"; // Act var actualText = Facebook.GetInitializationScripts(); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void LoginButtonTest() { // Arrange var expected40 = @"Awesome "button text""; var expected45 = @"Awesome "button text""; // Act var actualText = Facebook.LoginButton("http://www.test.com", "http://ww.test.com/Login.cshtml", "http://www.test.com/facebook/", "Awesome \"button text\"", true, "extra-small", "extra-long", true, "none\""); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(RuntimeEnvironment.IsVersion45Installed ? expected45 : expected40, actualText.ToString()); } [Fact] public void LoginButtonOnlyTagTest() { // Arrange var expectedText = @""Awesome button text""; // Act var actualText = Facebook.LoginButtonTagOnly("\"Awesome button text\"", true, "small", "medium", "foobar();", true, "none\""); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void LikeButtonTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.LikeButton("http://somewebsite", "modern", false, 300, 30, "hop", "Comic Sans", "lighter", "foo bar"); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void CommentsWithNoXidTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.Comments(null, 300, 3, true, true); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void CommentsWithXidTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.Comments("bar", 300, 3, true, true); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void RecommendationsTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.Recommendations("http://somesite", 100, 200, false, "blue", "none", "black", "All posts", "ref label"); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void LikeBoxTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.LikeBox("http://somesite", 100, 200, "blue", 5, true, false); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void FacepileTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.Facepile(3, 100); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void LiveStreamWithEmptyXidTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.LiveStream(100, 100, "", "", true); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void LiveStreamWithXidValueTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.LiveStream(100, 100, "some-val", "http://mysite", true); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void ActivityStreamTest() { // Arrange var expectedText = @""; // Act var actualText = Facebook.ActivityFeed("http://mysite", 100, 120, false, "gray", "Arial", "blue", true); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedText, actualText.ToString()); } [Fact] public void FbmlNamespacesTest() { // Arrange var expectedText = @"xmlns:fb=""http://www.facebook.com/2008/fbml"" xmlns:og=""http://opengraphprotocol.org/schema/"""; // Act var actualText = Facebook.FbmlNamespaces(); // Assert Assert.Equal(expectedText, actualText.ToString()); } private static HttpContextBase CreateHttpContext(IDictionary cookieValues = null) { var context = new Mock(); var httpRequest = new Mock(); var cookies = new HttpCookieCollection(); httpRequest.Setup(c => c.Cookies).Returns(cookies); context.Setup(c => c.Request).Returns(httpRequest.Object); if (cookieValues != null) { foreach (var item in cookieValues) { cookies.Add(new HttpCookie(item.Key, item.Value)); } } return context.Object; } public void Dispose() { // Reset ScopeStorage (written via e.g. Facebook.AppId) between tests to avoid unexpected interactions. ScopeStorage.CurrentProvider = new StaticScopeStorageProvider(); ScopeStorage.GlobalScope.Clear(); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/FileUploadTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Web; using System.Web.Helpers.Test; using System.Web.TestUtil; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { public class FileUploadTest { private const string _fileUploadScript = ""; [Fact] public void RenderThrowsWhenNumberOfFilesIsLessThanZero() { // Act and Assert Assert.ThrowsArgumentGreaterThanOrEqualTo( () => FileUpload._GetHtml(GetContext(), name: null, initialNumberOfFiles: -2, allowMoreFilesToBeAdded: false, includeFormTag: false, addText: "", uploadText: "").ToString(), "initialNumberOfFiles", "0"); } [Fact] public void ResultIncludesFormTagAndSubmitButtonWhenRequested() { // Arrange string expectedResult = @"
" + @"
" + @"
"; // Act var actualResult = FileUpload._GetHtml(GetContext(), name: null, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: false, includeFormTag: true, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, actualResult.ToString()); } [Fact] public void ResultDoesNotIncludeFormTagAndSubmitButtonWhenNotRequested() { // Arrange string expectedResult = @"
"; // Act var actualResult = FileUpload._GetHtml(GetContext(), name: null, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: false, includeFormTag: false, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, actualResult.ToString()); } [Fact] public void ResultIncludesCorrectNumberOfInputFields() { // Arrange string expectedResult = @"
" + @"
"; // Act var actualResult = FileUpload._GetHtml(GetContext(), name: null, initialNumberOfFiles: 3, allowMoreFilesToBeAdded: false, includeFormTag: false, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, actualResult.ToString()); } [Fact] public void ResultIncludesAnchorTagWithCorrectAddText() { // Arrange string customAddText = "Add More"; string expectedResult = _fileUploadScript + @"
" + @""; // Act var result = FileUpload._GetHtml(GetContext(), name: null, allowMoreFilesToBeAdded: true, includeFormTag: false, addText: customAddText, initialNumberOfFiles: 1, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, result.ToString()); } [Fact] public void ResultDoesNotIncludeAnchorTagNorAddTextWhenNotRequested() { // Arrange string customAddText = "Add More"; string expectedResult = @"
"; // Act var result = FileUpload._GetHtml(GetContext(), name: null, allowMoreFilesToBeAdded: false, includeFormTag: false, addText: customAddText, uploadText: null, initialNumberOfFiles: 1); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, result.ToString()); } [Fact] public void ResultIncludesSubmitInputTagWithCustomUploadText() { // Arrange string customUploadText = "Now!"; string expectedResult = @"
" + @"
" + @"
"; // Act var result = FileUpload._GetHtml(GetContext(), name: null, includeFormTag: true, uploadText: customUploadText, allowMoreFilesToBeAdded: false, initialNumberOfFiles: 1, addText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, result.ToString()); } [Fact] public void FileUploadGeneratesUniqueIdsForMultipleCallsForCommonRequest() { // Arrange var context = GetContext(); string expectedResult1 = @"
" + @"
"; string expectedResult2 = @"
" + @"
"; // Act var result1 = FileUpload._GetHtml(context, name: null, initialNumberOfFiles: 3, allowMoreFilesToBeAdded: false, includeFormTag: false, addText: null, uploadText: null); var result2 = FileUpload._GetHtml(context, name: null, initialNumberOfFiles: 2, allowMoreFilesToBeAdded: false, includeFormTag: true, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult1, result1.ToString()); UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult2, result2.ToString()); } [Fact] public void FileUploadGeneratesScriptOncePerRequest() { // Arrange var context = GetContext(); string expectedResult1 = _fileUploadScript + @"
" + @""; string expectedResult2 = @"
" + @"
"; string expectedResult3 = @"
" + @"
" + @"
"; // Act var result1 = FileUpload._GetHtml(context, name: null, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: true, includeFormTag: false, addText: null, uploadText: null); var result2 = FileUpload._GetHtml(context, name: null, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: true, includeFormTag: true, addText: null, uploadText: null); var result3 = FileUpload._GetHtml(context, name: null, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: false, includeFormTag: true, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult1, result1.ToString()); UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult2, result2.ToString()); UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult3, result3.ToString()); } [Fact] public void FileUploadUsesNamePropertyInJavascript() { // Arrange var context = GetContext(); string name = "foobar"; string expectedResult = _fileUploadScript + @"
" + @"
"; // Act var result = FileUpload._GetHtml(context, name: name, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: true, includeFormTag: true, addText: null, uploadText: null); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedResult, result.ToString()); } [Fact] public void _GetHtmlWithDefaultArgumentsProducesValidXhtml() { // Act var result = FileUpload._GetHtml(GetContext(), name: null, initialNumberOfFiles: 1, includeFormTag: false, allowMoreFilesToBeAdded: false, addText: null, uploadText: null); // Assert XhtmlAssert.Validate1_1(result, "div"); } [Fact] public void _GetHtmlWithoutFormTagProducesValidXhtml() { // Act var result = FileUpload._GetHtml(GetContext(), name: null, includeFormTag: false, initialNumberOfFiles: 1, allowMoreFilesToBeAdded: false, addText: null, uploadText: null); XhtmlAssert.Validate1_1(result, "div"); } private HttpContextBase GetContext() { var context = new Mock(); context.Setup(c => c.Items).Returns(new Hashtable()); return context.Object; } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/GamerCardTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Helpers.Test; using System.Web.TestUtil; using Microsoft.TestCommon; namespace Microsoft.Web.Helpers.Test { public class GamerCardTest { [Fact] public void RenderThrowsWhenGamertagIsEmpty() { // Act & Assert Assert.ThrowsArgumentNullOrEmptyString(() => { GamerCard.GetHtml(String.Empty).ToString(); }, "gamerTag"); } [Fact] public void RenderThrowsWhenGamertagIsNull() { // Act & Assert Assert.ThrowsArgumentNullOrEmptyString(() => { GamerCard.GetHtml(null).ToString(); }, "gamerTag"); } [Fact] public void RenderGeneratesProperMarkupWithSimpleGamertag() { // Arrange string expectedHtml = ""; // Act string html = GamerCard.GetHtml("osbornm").ToHtmlString(); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedHtml, html); } [Fact] public void RenderGeneratesProperMarkupWithComplexGamertag() { // Arrange string expectedHtml = ""; // Act string html = GamerCard.GetHtml("matthew osborn's").ToHtmlString(); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedHtml, html); } [Fact] public void RenderGeneratesValidXhtml() { XhtmlAssert.Validate1_0( GamerCard.GetHtml("osbornm") ); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/GravatarTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Helpers.Test; using Microsoft.TestCommon; namespace Microsoft.Web.Helpers.Test { public class GravatarTest { [Fact] public void GetUrlDefaults() { string url = Gravatar.GetUrl("foo@bar.com"); Assert.Equal("http://www.gravatar.com/avatar/f3ada405ce890b6f8204094deb12d8a8?s=80", url); } [Fact] public void RenderEncodesDefaultImageUrl() { string render = Gravatar.GetHtml("foo@bar.com", defaultImage: "http://example.com/images/example.jpg").ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderLowerCasesEmail() { string render = Gravatar.GetHtml("FOO@BAR.COM").ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RendersValidXhtml() { XhtmlAssert.Validate1_1(Gravatar.GetHtml("foo@bar.com")); } [Fact] public void RenderThrowsWhenEmailIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Gravatar.GetHtml(String.Empty); }, "email"); } [Fact] public void RenderThrowsWhenEmailIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Gravatar.GetHtml(null); }, "email"); } [Fact] public void RenderThrowsWhenImageSizeIsLessThanZero() { Assert.ThrowsArgument(() => { Gravatar.GetHtml("foo@bar.com", imageSize: -1); }, "imageSize", "The Gravatar image size must be between 1 and 512 pixels."); } [Fact] public void RenderThrowsWhenImageSizeIsZero() { Assert.ThrowsArgument(() => { Gravatar.GetHtml("foo@bar.com", imageSize: 0); }, "imageSize", "The Gravatar image size must be between 1 and 512 pixels."); } [Fact] public void RenderThrowsWhenImageSizeIsGreaterThan512() { Assert.ThrowsArgument(() => { Gravatar.GetHtml("foo@bar.com", imageSize: 513); }, "imageSize", "The Gravatar image size must be between 1 and 512 pixels."); } [Fact] public void RenderTrimsEmail() { string render = Gravatar.GetHtml(" \t foo@bar.com\t\r\n").ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderUsesDefaultImage() { string render = Gravatar.GetHtml("foo@bar.com", defaultImage: "wavatar").ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderUsesImageSize() { string render = Gravatar.GetHtml("foo@bar.com", imageSize: 512).ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderUsesRating() { string render = Gravatar.GetHtml("foo@bar.com", rating: GravatarRating.G).ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderWithAttributes() { string render = Gravatar.GetHtml("foo@bar.com", attributes: new { id = "gravatar", alT = "foo@bar.com", srC = "ignored" }).ToString(); // beware of attributes ordering in tests Assert.Equal( "\"<bfoo@bar.com</b>\" id=\"gravatar\" />", render); } [Fact] public void RenderWithDefaults() { string render = Gravatar.GetHtml("foo@bar.com").ToString(); Assert.Equal( "\"gravatar\"", render); } [Fact] public void RenderWithExtension() { string render = Gravatar.GetHtml("foo@bar.com", imageExtension: ".png").ToString(); Assert.Equal( "\"gravatar\"", render); render = Gravatar.GetHtml("foo@bar.com", imageExtension: "xyz").ToString(); Assert.Equal( "\"gravatar\"", render); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/LinkShareTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Web; using System.Web.Helpers.Test; using System.Web.TestUtil; using System.Web.WebPages.Scope; using Microsoft.TestCommon; namespace Microsoft.Web.Helpers.Test { [Xunit.Collection("Uses ScopeStorage or ViewEngines.Engines")] public class LinkShareTest : IDisposable { private static LinkShareSite[] _allLinkShareSites = new[] { LinkShareSite.Delicious, LinkShareSite.Digg, LinkShareSite.Facebook, LinkShareSite.Reddit, LinkShareSite.StumbleUpon, LinkShareSite.Twitter }; [Fact] public void RenderWithFacebookFirst_ReturnsHtmlWithFacebookAndThenOthersTest() { string pageTitle = "page1"; string pageLinkBack = "page link back"; string twitterUserName = String.Empty; string twitterTag = String.Empty; string actual; actual = LinkShare.GetHtml(pageTitle, pageLinkBack, twitterUserName, twitterTag, LinkShareSite.Facebook, LinkShareSite.All).ToString(); Assert.Contains("twitter.com", actual); int pos = actual.IndexOf("facebook.com"); Assert.True(pos > 0); int pos2 = actual.IndexOf("reddit.com"); Assert.True(pos2 > pos); pos2 = actual.IndexOf("digg.com"); Assert.True(pos2 > pos); } [Fact] public void BitlyApiKeyThrowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => LinkShare.BitlyApiKey = null, "value"); } [Fact] public void BitlyApiKeyUsesScopeStorage() { // Arrange var value = "value"; // Act LinkShare.BitlyApiKey = value; // Assert Assert.Equal(LinkShare.BitlyApiKey, value); Assert.Equal(ScopeStorage.CurrentScope[LinkShare._bitlyApiKey], value); } [Fact] public void BitlyLoginThrowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => LinkShare.BitlyLogin = null, "value"); } [Fact] public void BitlyLoginUsesScopeStorage() { // Arrange var value = "value"; // Act LinkShare.BitlyLogin = value; // Assert Assert.Equal(LinkShare.BitlyLogin, value); Assert.Equal(ScopeStorage.CurrentScope[LinkShare._bitlyLogin], value); } [Fact] public void RenderWithNullPageTitle_ThrowsException() { Assert.ThrowsArgumentNullOrEmptyString( () => LinkShare.GetHtml(null).ToString(), "pageTitle"); } [Fact] public void Render_WithFacebook_Works() { string actualHTML = LinkShare.GetHtml("page-title", "www.foo.com", linkSites: LinkShareSite.Facebook).ToString(); string expectedHTML = "\"Share"; UnitTestHelper.AssertEqualsIgnoreWhitespace(actualHTML, expectedHTML); } [Fact] public void Render_WithFacebookAndDigg_Works() { string actualHTML = LinkShare.GetHtml("page-title", "www.foo.com", linkSites: new[] { LinkShareSite.Facebook, LinkShareSite.Digg }).ToString(); string expectedHTML = "\"Share\"Digg!\""; UnitTestHelper.AssertEqualsIgnoreWhitespace(actualHTML, expectedHTML); } [Fact] public void Render_WithFacebook_RendersAnchorTitle() { string actualHTML = LinkShare.GetHtml("page-title", "www.foo.com", linkSites: LinkShareSite.Facebook).ToString(); string expectedHtml = @" "; UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedHtml, actualHTML); } [Fact] public void LinkShare_GetSitesInOrderReturnsAllSitesWhenArgumentIsNull() { // Act and Assert var result = LinkShare.GetSitesInOrder(linkSites: null); Assert.Equal(_allLinkShareSites, result.ToArray()); } [Fact] public void LinkShare_GetSitesInOrderReturnsAllSitesWhenArgumentIEmpty() { // Act var result = LinkShare.GetSitesInOrder(linkSites: new LinkShareSite[] { }); // Assert Assert.Equal(_allLinkShareSites, result.ToArray()); } [Fact] public void LinkShare_GetSitesInOrderDoesNotReturnsGoogleBuzzForAll() { // Act var result = LinkShare.GetSitesInOrder(linkSites: new LinkShareSite[] { LinkShareSite.All }); // Assert // 2 is the deprecated value for GoogleBuzz Assert.DoesNotContain(((LinkShareSite)2), result.ToArray()); } [Fact] public void LinkShare_GetSitesInOrderReturnsAllSitesWhenAllIsFirstItem() { // Act var result = LinkShare.GetSitesInOrder(linkSites: new[] { LinkShareSite.All, LinkShareSite.Reddit }); // Assert Assert.Equal(_allLinkShareSites, result.ToArray()); } [Fact] public void LinkShare_GetSitesInOrderReturnsSitesInOrderWhenAllIsNotFirstItem() { // Act var result = LinkShare.GetSitesInOrder(linkSites: new[] { LinkShareSite.Reddit, LinkShareSite.Facebook, LinkShareSite.All }); // Assert Assert.Equal(new[] { LinkShareSite.Reddit, LinkShareSite.Facebook, LinkShareSite.Delicious, LinkShareSite.Digg, LinkShareSite.StumbleUpon, LinkShareSite.Twitter }, result.ToArray()); } [Fact] public void LinkShare_EncodesParameters() { // Arrange var expectedHtml = @" "; // Act var actualHtml = LinkShare.GetHtml("&&", "www.foo.com", "", "I <3 Tweets", LinkShareSite.Reddit, LinkShareSite.Twitter).ToString(); // Assert UnitTestHelper.AssertEqualsIgnoreWhitespace(expectedHtml, actualHtml); } [Fact] public void LinkshareRendersValidXhtml() { string result = " \n \n \n
\n" + LinkShare.GetHtml("any<>title", "my test page <>") + "\n
\n "; HtmlString htmlResult = new HtmlString(result); XhtmlAssert.Validate1_0(htmlResult); } public void Dispose() { // Reset ScopeStorage (written via e.g. LinkShare.BitlyApiKey) between tests to avoid unexpected interactions. ScopeStorage.CurrentProvider = new StaticScopeStorageProvider(); ScopeStorage.GlobalScope.Clear(); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/MapsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Linq; using System.Web; using System.Web.WebPages.TestUtils; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { public class MapsTest { [Fact] public void GetDirectionsQueryReturnsLocationIfNotEmpty() { // Arrange var location = "a%"; var latitude = "12.34"; var longitude = "-56.78"; // Act string result = Maps.GetDirectionsQuery(location, latitude, longitude); // Assert Assert.Equal("a%25", result); } [Fact] public void GetDirectionsQueryReturnsLatitudeLongitudeIfLocationIsEmpty() { // Arrange var location = ""; var latitude = "12.34%"; var longitude = "-&56.78"; // Act string result = Maps.GetDirectionsQuery(location, latitude, longitude); // Assert Assert.Equal("12.34%25%2c-%2656.78", result); } [Fact] public void GetDirectionsQueryUsesSpecifiedEncoder() { // Arrange var location = "24 gnidliuB tfosorciM"; var latitude = "12.34%"; var longitude = "-&56.78"; Func encoder = k => new String(k.Reverse().ToArray()); // Act string result = Maps.GetDirectionsQuery(location, latitude, longitude, encoder); // Assert Assert.Equal("Microsoft Building 42", result); } [Fact] public void GetProviderHtml_DoesNotContainBadRazorCompilation() { AppDomainUtils.RunInSeparateAppDomain(() => { // Arrange var stubbedContext = new Mock(); var contextItems = new Hashtable(); stubbedContext.SetupGet(x => x.Items).Returns(contextItems); Maps.GetCurrentHttpContext = () => stubbedContext.Object; // Act string bingResults = Maps.GetBingHtml("somekey", latitude: "100", longitude: "10").ToHtmlString(); string googleResults = Maps.GetGoogleHtml(latitude: "100", longitude: "10").ToHtmlString(); string mapQuestResults = Maps.GetMapQuestHtml("somekey", latitude: "100", longitude: "10").ToHtmlString(); // Assert Assert.DoesNotContain("", bingResults); Assert.DoesNotContain("", googleResults); Assert.DoesNotContain("", mapQuestResults); }); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/Microsoft.Web.Helpers.Test.csproj ================================================  {2C653A66-8159-4A41-954F-A67915DFDA87} Library Properties Microsoft.Web.Helpers.Test Microsoft.Web.Helpers.Test ..\..\bin\$(Configuration)\Test\ ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True {0C7CE809-0F72-4C19-8C64-D6573E4D9521} Microsoft.Web.Helpers {9B7E3740-6161-4548-833C-4BBCA43B970E} System.Web.Helpers {8F18041B-9410-4C36-A9C5-067813DF5F31} System.Web.Razor {0939B11A-FE4E-4BA1-8AD6-D97741EE314F} System.Web.WebPages.Razor {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} Microsoft.TestCommon {D3313BDF-8071-4AC8-9D98-ABF7F9E88A57} System.Web.Helpers.Test This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/Microsoft.Web.Helpers.Test/PreAppStartCodeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.WebPages.Razor; using System.Web.WebPages.TestUtils; using Microsoft.TestCommon; namespace Microsoft.Web.Helpers.Test { public class PreApplicationStartCodeTest { [Fact] public void StartTest() { AppDomainUtils.RunInSeparateAppDomain(() => { // Act AppDomainUtils.SetPreAppStartStage(); PreApplicationStartCode.Start(); // Assert var imports = WebPageRazorHost.GetGlobalImports(); Assert.Contains(imports, ns => ns.Equals("Microsoft.Web.Helpers")); }); } [Fact] public void TestPreAppStartClass() { PreAppStartTestHelper.TestPreAppStartClass(typeof(PreApplicationStartCode)); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/ReCaptchaTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Web; using System.Web.Helpers.Test; using System.Web.TestUtil; using System.Web.WebPages.Scope; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { [Xunit.Collection("Uses ScopeStorage or ViewEngines.Engines")] public class ReCaptchaTest : IDisposable { [Fact] public void ReCaptchaOptionsMissingWhenNoOptionsAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenOneOptionAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: new { theme = "white" }); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenMultipleOptionsAndDefaultRendering() { var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: new { theme = "white", tabindex = 5 }); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ReCaptchaOptionsWhenMultipleOptionsFromDictionaryAndDefaultRendering() { // verifies that a dictionary will serialize the same as a projection var options = new Dictionary { { "theme", "white" }, { "tabindex", 5 } }; var html = ReCaptcha.GetHtml(GetContext(), "PUBLIC_KEY", options: options); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void RenderUsesLastError() { HttpContextBase context = GetContext(); ReCaptcha.HandleValidateResponse(context, "false\nincorrect-captcha-sol"); var html = ReCaptcha.GetHtml(context, "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void RenderWhenConnectionIsSecure() { var html = ReCaptcha.GetHtml(GetContext(isSecure: true), "PUBLIC_KEY"); UnitTestHelper.AssertEqualsIgnoreWhitespace( @"" + @"", html.ToString()); XhtmlAssert.Validate1_0(html, addRoot: true); } [Fact] public void ValidateThrowsWhenRemoteAddressNotAvailable() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; context.Request.Form["recaptcha_response_field"] = "RESPONSE"; Assert.Throws(() => { ReCaptcha.Validate(context, privateKey: "PRIVATE_KEY", virtualPathUtility: virtualPathUtility).ToString(); }, "The captcha cannot be validated because the remote address was not found in the request."); } [Fact] public void ValidateReturnsFalseWhenChallengeNotPosted() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; Assert.False(ReCaptcha.Validate(context, privateKey: "PRIVATE_KEY", virtualPathUtility: virtualPathUtility)); } [Fact] public void ValidatePostData() { HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; context.Request.Form["recaptcha_response_field"] = "RESPONSE"; Assert.Equal("privatekey=PRIVATE_KEY&remoteip=127.0.0.1&challenge=CHALLENGE&response=RESPONSE", ReCaptcha.GetValidatePostData(context, "PRIVATE_KEY", virtualPathUtility)); } [Fact] public void ValidatePostDataWhenNoResponse() { // Arrange HttpContextBase context = GetContext(); VirtualPathUtilityBase virtualPathUtility = GetVirtualPathUtility(); context.Request.ServerVariables["REMOTE_ADDR"] = "127.0.0.1"; context.Request.Form["recaptcha_challenge_field"] = "CHALLENGE"; // Act var validatePostData = ReCaptcha.GetValidatePostData(context, "PRIVATE_KEY", virtualPathUtility); // Assert Assert.Equal("privatekey=PRIVATE_KEY&remoteip=127.0.0.1&challenge=CHALLENGE&response=", validatePostData); } [Fact] public void ValidateResponseReturnsFalseOnEmptyReCaptchaResponse() { HttpContextBase context = GetContext(); Assert.False(ReCaptcha.HandleValidateResponse(context, "")); Assert.Equal(String.Empty, ReCaptcha.GetLastError(context)); } [Fact] public void ValidateResponseReturnsTrueOnSuccess() { HttpContextBase context = GetContext(); Assert.True(ReCaptcha.HandleValidateResponse(context, "true\nsuccess")); Assert.Equal(String.Empty, ReCaptcha.GetLastError(context)); } [Fact] public void ValidateResponseReturnsFalseOnError() { HttpContextBase context = GetContext(); Assert.False(ReCaptcha.HandleValidateResponse(context, "false\nincorrect-captcha-sol")); Assert.Equal("incorrect-captcha-sol", ReCaptcha.GetLastError(context)); } [Fact] public void ReCaptchaPrivateKeyThowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => ReCaptcha.PrivateKey = null, "value"); } [Fact] public void ReCaptchaPrivateKeyUsesScopeStorage() { // Arrange var value = "value"; // Act ReCaptcha.PrivateKey = value; // Assert Assert.Equal(ReCaptcha.PrivateKey, value); Assert.Equal(ScopeStorage.CurrentScope[ReCaptcha._privateKey], value); } [Fact] public void PublicKeyThowsWhenSetToNull() { Assert.ThrowsArgumentNull(() => ReCaptcha.PublicKey = null, "value"); } [Fact] public void ReCaptchaPublicKeyUsesScopeStorage() { // Arrange var value = "value"; // Act ReCaptcha.PublicKey = value; // Assert Assert.Equal(ReCaptcha.PublicKey, value); Assert.Equal(ScopeStorage.CurrentScope[ReCaptcha._publicKey], value); } private HttpContextBase GetContext(bool isSecure = false) { // mock HttpRequest Mock requestMock = new Mock(); requestMock.Setup(request => request.IsSecureConnection).Returns(isSecure); requestMock.Setup(request => request.Form).Returns(new NameValueCollection()); requestMock.Setup(request => request.ServerVariables).Returns(new NameValueCollection()); // mock HttpContext Mock contextMock = new Mock(); contextMock.Setup(context => context.Items).Returns(new Hashtable()); contextMock.Setup(context => context.Request).Returns(requestMock.Object); return contextMock.Object; } private static VirtualPathUtilityBase GetVirtualPathUtility() { var virtualPathUtility = new Mock(); virtualPathUtility.Setup(c => c.ToAbsolute(It.IsAny())).Returns(_ => _); return virtualPathUtility.Object; } public void Dispose() { // Reset ScopeStorage (written via e.g. ReCaptcha.PrivateKey) between tests to avoid unexpected interactions. ScopeStorage.CurrentProvider = new StaticScopeStorageProvider(); ScopeStorage.GlobalScope.Clear(); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/ThemesTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Hosting; using System.Web.WebPages.Scope; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { public class ThemesTest { [Fact] public void Initialize_WithBadParams_Throws() { // Arrange var mockVpp = new Mock().Object; var scope = new ScopeStorageDictionary(); // Act and Assert Assert.ThrowsArgumentNullOrEmptyString(() => new ThemesImplementation(mockVpp, scope).Initialize(null, "foo"), "themeDirectory"); Assert.ThrowsArgumentNullOrEmptyString(() => new ThemesImplementation(mockVpp, scope).Initialize("", "foo"), "themeDirectory"); Assert.ThrowsArgumentNullOrEmptyString(() => new ThemesImplementation(mockVpp, scope).Initialize("~/folder", null), "defaultTheme"); Assert.ThrowsArgumentNullOrEmptyString(() => new ThemesImplementation(mockVpp, scope).Initialize("~/folder", ""), "defaultTheme"); } [Fact] public void CurrentThemeThrowsIfAssignedNullOrEmpty() { // Arrange var mockVpp = new Mock().Object; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(mockVpp, scope); // Act and Assert Assert.ThrowsArgumentNullOrEmptyString(() => { themesImpl.CurrentTheme = null; }, "value"); Assert.ThrowsArgumentNullOrEmptyString(() => { themesImpl.CurrentTheme = String.Empty; }, "value"); } [Fact] public void InvokingPropertiesAndMethodsBeforeInitializationThrows() { // Arrange var mockVpp = new Mock().Object; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(mockVpp, scope); // Act and Assert Assert.Throws(() => themesImpl.CurrentTheme = "Foo", @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); Assert.Throws(() => { var x = themesImpl.CurrentTheme; }, @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); Assert.Throws(() => { var x = themesImpl.AvailableThemes; }, @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); Assert.Throws(() => { var x = themesImpl.DefaultTheme; }, @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); Assert.Throws(() => { var x = themesImpl.GetResourcePath("baz"); }, @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); Assert.Throws(() => { var x = themesImpl.GetResourcePath("baz", "some-file"); }, @"You must call the ""Themes.Initialize"" method before you call any other method of the ""Themes"" class."); } [Fact] public void InitializeThrowsIfDefaultThemeDirectoryDoesNotExist() { // Arrange var defaultTheme = "default-theme"; var themeDirectory = "theme-directory"; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(GetVirtualPathProvider(themeDirectory, new Dir("not-default-theme")), scope); // Act And Assert Assert.ThrowsArgument( () => themesImpl.Initialize(themeDirectory: themeDirectory, defaultTheme: defaultTheme), "defaultTheme", "Unknown theme 'default-theme'. Ensure that a directory labeled 'default-theme' exists under the theme directory."); } [Fact] public void ThemesImplUsesScopeStorageToStoreProperties() { // Arrange var defaultTheme = "default-theme"; var themeDirectory = "theme-directory"; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(GetVirtualPathProvider(themeDirectory, new Dir(defaultTheme)), scope); // Act themesImpl.Initialize(themeDirectory: themeDirectory, defaultTheme: defaultTheme); // Ensure Theme use scope storage to store properties Assert.Equal((object)true, scope[ThemesImplementation.ThemesInitializedKey]); Assert.Equal(themeDirectory, scope[ThemesImplementation.ThemeDirectoryKey]); Assert.Equal(defaultTheme, scope[ThemesImplementation.DefaultThemeKey]); } [Fact] public void ThemesImplUsesDefaultThemeWhenNoCurrentThemeIsSpecified() { // Arrange var defaultTheme = "default-theme"; var themeDirectory = "theme-directory"; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(GetVirtualPathProvider(themeDirectory, new Dir(defaultTheme)), scope); themesImpl.Initialize(themeDirectory, defaultTheme); // Act and Assert // CurrentTheme falls back to default theme when null Assert.Equal(themesImpl.CurrentTheme, defaultTheme); } [Fact] public void ThemesImplThrowsIfCurrentThemeIsInvalid() { // Arrange var defaultTheme = "default-theme"; var themeDirectory = "theme-directory"; var themesImpl = new ThemesImplementation(GetVirtualPathProvider(themeDirectory, new Dir(defaultTheme), new Dir("not-a-random-value")), new ScopeStorageDictionary()); themesImpl.Initialize(themeDirectory, defaultTheme); // Act and Assert Assert.ThrowsArgument(() => themesImpl.CurrentTheme = "random-value", "value", "Unknown theme 'random-value'. Ensure that a directory labeled 'random-value' exists under the theme directory."); } [Fact] public void ThemesImplUsesScopeStorageToStoreCurrentTheme() { // Arrange var defaultTheme = "default-theme"; var themeDirectory = "theme-directory"; var currentThemeDir = "custom-theme-dir"; var scope = new ScopeStorageDictionary(); var themesImpl = new ThemesImplementation(GetVirtualPathProvider(themeDirectory, new Dir(defaultTheme), new Dir("custom-theme-dir")), scope); // Act themesImpl.Initialize(themeDirectory, defaultTheme); themesImpl.CurrentTheme = currentThemeDir; // Assert Assert.Equal(scope[ThemesImplementation.CurrentThemeKey], currentThemeDir); } [Fact] public void GetResourcePathThrowsIfCurrentDirectoryIsNull() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile", "wp7.css"))); themesImpl.Initialize("themes", "default"); // Act and Assert Assert.ThrowsArgumentNull(() => themesImpl.GetResourcePath(folder: null, fileName: "wp7.css"), "folder"); } [Fact] public void GetResourcePathThrowsIfFileNameIsNullOrEmpty() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile", "wp7.css"))); themesImpl.Initialize("themes", "default"); // Act and Assert Assert.ThrowsArgumentNullOrEmptyString(() => themesImpl.GetResourcePath(folder: String.Empty, fileName: null), "fileName"); Assert.ThrowsArgumentNullOrEmptyString(() => themesImpl.GetResourcePath(folder: String.Empty, fileName: String.Empty), "fileName"); } [Fact] public void GetResourcePathReturnsItemFromThemeRootIfAvailable() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile", "wp7.css"))); themesImpl.Initialize("themes", "default"); // Act themesImpl.CurrentTheme = "mobile"; var themePath = themesImpl.GetResourcePath(fileName: "wp7.css"); // Assert Assert.Equal(@"themes/mobile/wp7.css", themePath); } [Fact] public void GetResourcePathReturnsItemFromCurrentThemeDirectoryIfAvailable() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile\styles", "wp7.css"), new Dir(@"default\styles", "main.css"))); themesImpl.Initialize("themes", "default"); // Act themesImpl.CurrentTheme = "mobile"; var themePath = themesImpl.GetResourcePath(folder: "styles", fileName: "wp7.css"); // Assert Assert.Equal(@"themes/mobile/styles/wp7.css", themePath); } [Fact] public void GetResourcePathReturnsItemFromDefaultThemeDirectoryIfNotFoundInCurrentThemeDirectory() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile\styles", "wp7.css"), new Dir(@"default\styles", "main.css"))); themesImpl.Initialize("themes", "default"); // Act themesImpl.CurrentTheme = "mobile"; var themePath = themesImpl.GetResourcePath(folder: "styles", fileName: "main.css"); // Assert Assert.Equal(@"themes/default/styles/main.css", themePath); } [Fact] public void GetResourcePathReturnsNullIfDirectoryDoesNotExist() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile\styles", "wp7.css"), new Dir(@"default\styles", "main.css"))); themesImpl.Initialize("themes", "default"); // Act themesImpl.CurrentTheme = "mobile"; var themePath = themesImpl.GetResourcePath(folder: "does-not-exist", fileName: "main.css"); // Assert Assert.Null(themePath); } [Fact] public void GetResourcePathReturnsNullIfItemNotFoundInCurrentAndDefaultThemeDirectories() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir(@"mobile\styles", "wp7.css"), new Dir(@"default\styles", "main.css"))); themesImpl.Initialize("themes", "default"); // Act themesImpl.CurrentTheme = "mobile"; var themePath = themesImpl.GetResourcePath(folder: "styles", fileName: "awesome-blinking-text.css"); // Assert Assert.Null(themePath); } [Fact] public void AvaliableThemesReturnsTopLevelDirectoriesUnderThemeDirectory() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("mobile"), new Dir("rotary-phone"))); // Act themesImpl.Initialize("themes", "default"); var themes = themesImpl.AvailableThemes; // Assert Assert.Equal(3, themes.Count); Assert.Equal("default", themes[0]); Assert.Equal("mobile", themes[1]); Assert.Equal("rotary-phone", themes[2]); } /// /// // folder structure: /// // /root /// // /foo /// // /bar.cs /// // testing that a file specified as foo/bar in folder root will return null /// [Fact] public void FileWithSlash_ReturnsNull() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("root"), new Dir(@"root\foo", "wp7.css"), new Dir(@"default\styles", "main.css"))); // Act var actual = themesImpl.FindMatchingFile("root", "foo/bar.cs"); // Assert Assert.Null(actual); } [Fact] public void DirectoryWithNoFilesReturnsNull() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir("default"), new Dir("empty-dir"))); // Act var actual = themesImpl.FindMatchingFile(@"themes\empty-dir", "main.css"); // Assert Assert.Null(actual); } [Fact] public void MatchingFiles_ReturnsCorrectFile() { // Arrange var themesImpl = new ThemesImplementation(scopeStorage: new ScopeStorageDictionary(), vpp: GetVirtualPathProvider("themes", new Dir(@"nomatchingfiles", "foo.cs"))); // Act var bar = themesImpl.FindMatchingFile(@"themes\nomatchingfiles", "bar.cs"); var foo = themesImpl.FindMatchingFile(@"themes\nomatchingfiles", "foo.cs"); // Assert Assert.Null(bar); Assert.Equal(@"themes/nomatchingfiles/foo.cs", foo); } private static VirtualPathProvider GetVirtualPathProvider(string themeRoot, params Dir[] fileSystem) { var mockVpp = new Mock(); var dirRoot = new Mock(themeRoot); var themeDirectories = new List(); foreach (var directory in fileSystem) { var dir = new Mock(directory.Name); var directoryPath = themeRoot + '\\' + directory.Name; dir.SetupGet(d => d.Name).Returns(directory.Name); mockVpp.Setup(c => c.GetDirectory(It.Is(p => p.Equals(directoryPath, StringComparison.OrdinalIgnoreCase)))).Returns(dir.Object); var fileList = new List(); foreach (var item in directory.Files) { var filePath = directoryPath + '\\' + item; var file = new Mock(filePath); file.SetupGet(f => f.Name).Returns(item); fileList.Add(file.Object); } dir.SetupGet(c => c.Files).Returns(fileList); themeDirectories.Add(dir.Object); } dirRoot.SetupGet(c => c.Directories).Returns(themeDirectories); mockVpp.Setup(c => c.GetDirectory(themeRoot)).Returns(dirRoot.Object); return mockVpp.Object; } private class Dir { public Dir(string name, params string[] files) { Name = name; Files = files; } public string Name { get; private set; } public IEnumerable Files { get; private set; } } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/UrlBuilderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.IO; using System.Reflection; using System.Text; using System.Web; using System.Web.UI; using System.Web.WebPages; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { public class UrlBuilderTest { private static TestVirtualPathUtility _virtualPathUtility = new TestVirtualPathUtility(); [Fact] public void UrlBuilderUsesPathAsIsIfPathIsAValidUri() { // Arrange var pagePath = "http://www.test.com/page-path"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); // Assert Assert.Equal("http://www.test.com/page-path", builder.Path); } [Fact] public void UrlBuilderUsesQueryComponentsIfPathIsAValidUri() { // Arrange var pagePath = "http://www.test.com/page-path.vbhtml?param=value&baz=biz"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); // Assert Assert.Equal("http://www.test.com/page-path.vbhtml", builder.Path); Assert.Equal("?param=value&baz=biz", builder.QueryString); } [Fact] public void UrlBuilderUsesUsesObjectParameterAsQueryString() { // Arrange var pagePath = "http://www.test.com/page-path.vbhtml?param=value&baz=biz"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); // Assert Assert.Equal("http://www.test.com/page-path.vbhtml", builder.Path); Assert.Equal("?param=value&baz=biz", builder.QueryString); } [Fact] public void UrlBuilderAppendsObjectParametersToPathWithQueryString() { // Arrange var pagePath = "http://www.test.com/page-path.vbhtml?param=value&baz=biz"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, new { param2 = "param2val" }); // Assert Assert.Equal("http://www.test.com/page-path.vbhtml", builder.Path); Assert.Equal("?param=value&baz=biz¶m2=param2val", builder.QueryString); } [Fact] public void UrlBuilderWithVirtualPathUsesVirtualPathUtility() { // Arrange var pagePath = "~/page"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); // Assert Assert.Equal("page", builder.Path); } [Fact] public void UrlBuilderDoesNotResolvePathIfContextIsNull() { // Arrange var pagePath = "~/foo/bar"; // Act var builder = new UrlBuilder(null, _virtualPathUtility, pagePath, null); // Assert Assert.Equal(pagePath, builder.Path); } [Fact] public void UrlBuilderSetsPathToNullIfContextIsNullAndPagePathIsNotSpecified() { // Act var builder = new UrlBuilder(null, null, null, null); // Assert Assert.Null(builder.Path); Assert.True(String.IsNullOrEmpty(builder.ToString())); } [Fact] public void UrlBuilderWithVirtualPathAppendsObjectAsQueryString() { // Arrange var pagePath = "~/page"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, new { Foo = "bar", baz = "qux" }); // Assert Assert.Equal("page", builder.Path); Assert.Equal("?Foo=bar&baz=qux", builder.QueryString); } [Fact] public void UrlBuilderWithVirtualPathExtractsQueryStringParameterFromPath() { // Arrange var pagePath = "~/dir/page?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); // Assert Assert.Equal("dir/page", builder.Path); Assert.Equal("?someparam=value", builder.QueryString); } [Fact] public void UrlBuilderWithVirtualPathAndQueryStringAppendsObjectAsQueryStringParams() { // Arrange var pagePath = "~/dir/page?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, new { someotherparam = "value2" }); // Assert Assert.Equal("dir/page", builder.Path); Assert.Equal("?someparam=value&someotherparam=value2", builder.QueryString); } [Fact] public void AddPathAddsPathPortionToRelativeUrl() { // Arrange var pagePath = "~/dir/page?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo").AddPath("bar/baz"); // Assert Assert.Equal("dir/page/foo/bar/baz", builder.Path); Assert.Equal("?someparam=value", builder.QueryString); } [Fact] public void AddPathEncodesPathParams() { // Arrange var pagePath = "~/dir/page?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo bar").AddPath("baz biz", "qux"); // Assert Assert.Equal("dir/page/foo%20bar/baz%20biz/qux", builder.Path); } [Fact] public void AddPathAddsPathPortionToAbsoluteUrl() { // Arrange var pagePath = "http://some-site/dir/page?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo").AddPath("bar/baz"); // Assert Assert.Equal("http://some-site/dir/page/foo/bar/baz", builder.Path); Assert.Equal("?someparam=value", builder.QueryString); } [Fact] public void AddPathWithParamsArrayAddsPathPortionToRelativeUrl() { // Arrange var pagePath = "~/dir/page/?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo", "bar", "baz").AddPath("qux"); // Assert Assert.Equal("dir/page/foo/bar/baz/qux", builder.Path); Assert.Equal("?someparam=value", builder.QueryString); } [Fact] public void AddPathEnsuresSlashesAreNotRepeated() { // Arrange var pagePath = "~/dir/page/"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo").AddPath("/bar/").AddPath("/baz"); // Assert Assert.Equal("dir/page/foo/bar/baz", builder.Path); } [Fact] public void AddPathWithParamsEnsuresSlashAreNotRepeated() { // Arrange var pagePath = "~/dir/page/"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo", "/bar/", "/baz").AddPath("qux/"); // Assert Assert.Equal("dir/page/foo/bar/baz/qux/", builder.Path); } [Fact] public void AddPathWithParamsArrayAddsPathPortionToAbsoluteUrl() { // Arrange var pagePath = "http://www.test.com/dir/page/?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddPath("foo", "bar", "baz").AddPath("qux"); // Assert Assert.Equal("http://www.test.com/dir/page/foo/bar/baz/qux", builder.Path); Assert.Equal("?someparam=value", builder.QueryString); } [Fact] public void UrlBuilderEncodesParameters() { // Arrange var pagePath = "~/dir/page/?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, new { Λ = "λ" }); builder.AddParam(new { π = "is not a lie" }).AddParam("Π", "maybe a lie"); // Assert Assert.Equal("?someparam=value&%ce%9b=%ce%bb&%cf%80=is+not+a+lie&%ce%a0=maybe+a+lie", builder.QueryString); } [Fact] public void AddParamAddsParamToQueryString() { // Arrange var pagePath = "http://www.test.com/dir/page/?someparam=value"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddParam("foo", "bar"); // Assert Assert.Equal("?someparam=value&foo=bar", builder.QueryString); } [Fact] public void AddParamAddsQuestionMarkToQueryStringIfFirstParam() { // Arrange var pagePath = "~/dir/page"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddParam("foo", "bar").AddParam(new { baz = "qux", biz = "quark" }); // Assert Assert.Equal("?foo=bar&baz=qux&biz=quark", builder.QueryString); } [Fact] public void AddParamIgnoresParametersWithEmptyKey() { // Arrange var pagePath = "~/dir/page"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddParam("", "bar").AddParam(new { baz = "", biz = "quark" }).AddParam("qux", null).AddParam(null, "somevalue"); // Assert Assert.Equal("?baz=&biz=quark&qux=", builder.QueryString); } [Fact] public void ToStringConcatsPathAndQueryString() { // Arrange var pagePath = "~/dir/page"; // Act var builder = new UrlBuilder(GetContext(), _virtualPathUtility, pagePath, null); builder.AddParam("foo", "bar").AddParam(new { baz = "qux", biz = "quark" }); // Assert Assert.Equal("dir/page?foo=bar&baz=qux&biz=quark", builder.ToString()); } [Fact] public void UrlBuilderWithRealVirtualPathUtilityTest() { // Arrange var pagePath = "~/world/test.aspx"; try { // Act CreateHttpContext("default.aspx", "http://localhost/WebSite1/subfolder1/default.aspx"); CreateHttpRuntime("/WebSite1/"); var builder = new UrlBuilder(pagePath, null); // Assert Assert.Equal(@"/WebSite1/world/test.aspx", builder.Path); } finally { RestoreHttpRuntime(); } } private static HttpContextBase GetContext(params string[] virtualPaths) { var httpContext = new Mock(); var table = new Hashtable(); httpContext.SetupGet(c => c.Items).Returns(table); foreach (var item in virtualPaths) { var page = new Mock(); page.SetupGet(p => p.TemplateInfo).Returns(new TemplateFileInfo(item)); TemplateStack.Push(httpContext.Object, page.Object); } return httpContext.Object; } private class TestVirtualPathUtility : VirtualPathUtilityBase { public override string Combine(string basePath, string relativePath) { return basePath + '/' + relativePath.TrimStart('~', '/'); } public override string ToAbsolute(string virtualPath) { return virtualPath.TrimStart('~', '/'); } } internal static void CreateHttpRuntime(string appVPath) { var runtime = new HttpRuntime(); var appDomainAppVPathField = typeof(HttpRuntime).GetField("_appDomainAppVPath", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); appDomainAppVPathField.SetValue(runtime, CreateVirtualPath(appVPath)); GetTheRuntime().SetValue(null, runtime); var appDomainIdField = typeof(HttpRuntime).GetField("_appDomainId", BindingFlags.NonPublic | BindingFlags.Instance); appDomainIdField.SetValue(runtime, "test"); } internal static FieldInfo GetTheRuntime() { return typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static); } internal static void RestoreHttpRuntime() { GetTheRuntime().SetValue(null, null); } // E.g. "default.aspx", "http://localhost/WebSite1/subfolder1/default.aspx" internal static void CreateHttpContext(string filename, string url) { var request = new HttpRequest(filename, url, null); var httpContext = new HttpContext(request, new HttpResponse(new StringWriter(new StringBuilder()))); HttpContext.Current = httpContext; } internal static void RestoreHttpContext() { HttpContext.Current = null; } internal static object CreateVirtualPath(string path) { var vPath = typeof(Page).Assembly.GetType("System.Web.VirtualPath"); var method = vPath.GetMethod("CreateNonRelativeTrailingSlash", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return method.Invoke(null, new object[] { path }); } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/VideoTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Reflection; using System.Text.RegularExpressions; using System.Web; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Helpers.Test { public class VideoTest { private VirtualPathUtilityWrapper _pathUtility = new VirtualPathUtilityWrapper(); [Fact] public void FlashCannotOverrideHtmlAttributes() { Assert.ThrowsArgument(() => { Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf", htmlAttributes: new { cLASSid = "CanNotOverride" }); }, "htmlAttributes", "Property \"cLASSid\" cannot be set through this argument."); } [Fact] public void FlashDefaults() { string html = Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf").ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.EndsWith("", html); } [Fact] public void FlashThrowsWhenPathIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Flash(GetContext(), _pathUtility, String.Empty); }, "path"); } [Fact] public void FlashThrowsWhenPathIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Flash(GetContext(), _pathUtility, null); }, "path"); } [Fact] public void FlashWithExposedOptions() { string html = Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf", width: "100px", height: "100px", play: false, loop: false, menu: false, backgroundColor: "#000", quality: "Q", scale: "S", windowMode: "WM", baseUrl: "http://foo.bar.com/", version: "1.0.0.0", htmlAttributes: new { id = "fl" }, embedName: "efl").ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); var embed = new Regex("").Match(html); Assert.True(embed.Success); Assert.StartsWith("", html); Assert.Contains("", html); // note - can't guarantee order of optional params: Assert.True( html.Contains("") || html.Contains("") ); } [Fact] public void MediaPlayerCannotOverrideHtmlAttributes() { Assert.ThrowsArgument(() => { Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv", htmlAttributes: new { cODEbase = "CanNotOverride" }); }, "htmlAttributes", "Property \"cODEbase\" cannot be set through this argument."); } [Fact] public void MediaPlayerDefaults() { string html = Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv").ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.EndsWith("", html); } [Fact] public void MediaPlayerThrowsWhenPathIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.MediaPlayer(GetContext(), _pathUtility, String.Empty); }, "path"); } [Fact] public void MediaPlayerThrowsWhenPathIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.MediaPlayer(GetContext(), _pathUtility, null); }, "path"); } [Fact] public void MediaPlayerWithExposedOptions() { string html = Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv", width: "100px", height: "100px", autoStart: false, playCount: 2, uiMode: "UIMODE", stretchToFit: true, enableContextMenu: false, mute: true, volume: 1, baseUrl: "http://foo.bar.com/", htmlAttributes: new { id = "mp" }, embedName: "emp").ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); var embed = new Regex("").Match(html); Assert.True(embed.Success); Assert.StartsWith("", html); Assert.Contains("", html); Assert.True( html.Contains("") || html.Contains("") ); } [Fact] public void SilverlightCannotOverrideHtmlAttributes() { Assert.ThrowsArgument(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", "100px", htmlAttributes: new { WIDTH = "CanNotOverride" }); }, "htmlAttributes", "Property \"WIDTH\" cannot be set through this argument."); } [Fact] public void SilverlightDefaults() { string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", "100px").ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("" + "\"Get", html); Assert.EndsWith("", html); } [Fact] public void SilverlightThrowsWhenPathIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, String.Empty, "100px", "100px"); }, "path"); } [Fact] public void SilverlightThrowsWhenPathIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, null, "100px", "100px"); }, "path"); } [Fact] public void SilverlightThrowsWhenHeightIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", String.Empty); }, "height"); } [Fact] public void SilverlightThrowsWhenHeightIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", null); }, "height"); } [Fact] public void SilverlightThrowsWhenWidthIsEmpty() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", String.Empty, "100px"); }, "width"); } [Fact] public void SilverlightThrowsWhenWidthIsNull() { Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", null, "100px"); }, "width"); } [Fact] public void SilverlightWithExposedOptions() { string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", width: "85%", height: "85%", backgroundColor: "red", initParameters: "X=Y", minimumVersion: "1.0.0.0", autoUpgrade: false, htmlAttributes: new { id = "sl" }).ToString().Replace("\r\n", ""); Assert.StartsWith("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); Assert.Contains("", html); var embed = new Regex("").Match(html); Assert.False(embed.Success); } [Fact] public void SilverlightWithUnexposedOptions() { string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", width: "50px", height: "50px", options: new { X = "Y", Z = 123 }).ToString().Replace("\r\n", ""); Assert.Contains("", html); Assert.Contains("", html); } [Fact] public void ValidatePathResolvesExistingLocalPath() { string path = Assembly.GetExecutingAssembly().Location; Mock pathUtility = new Mock(); pathUtility.Setup(p => p.Combine(It.IsAny(), It.IsAny())).Returns(path); pathUtility.Setup(p => p.ToAbsolute(It.IsAny())).Returns(path); Mock serverMock = new Mock(); serverMock.Setup(s => s.MapPath(It.IsAny())).Returns(path); HttpContextBase context = GetContext(serverMock.Object); string html = Video.Flash(context, pathUtility.Object, "foo.bar").ToString(); Assert.StartsWith(" pathUtility = new Mock(); pathUtility.Setup(p => p.Combine(It.IsAny(), It.IsAny())).Returns(path); pathUtility.Setup(p => p.ToAbsolute(It.IsAny())).Returns(path); Mock serverMock = new Mock(); serverMock.Setup(s => s.MapPath(It.IsAny())).Returns(path); HttpContextBase context = GetContext(serverMock.Object); Assert.Throws(() => { Video.Flash(context, pathUtility.Object, "exist.swf"); }, "The media file \"exist.swf\" does not exist."); } private static HttpContextBase GetContext(HttpServerUtilityBase serverUtility = null) { // simple mocked context - won't reference as long as path starts with 'http' Mock requestMock = new Mock(); Mock contextMock = new Mock(); contextMock.Setup(context => context.Request).Returns(requestMock.Object); contextMock.Setup(context => context.Server).Returns(serverUtility); return contextMock.Object; } } } ================================================ FILE: test/Microsoft.Web.Helpers.Test/packages.config ================================================  ================================================ FILE: test/Microsoft.Web.Mvc.Test/Controls/Test/DesignModeSite.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; namespace Microsoft.Web.Mvc.Controls.Test { public class DesignModeSite : ISite { IComponent ISite.Component { get { throw new NotImplementedException(); } } IContainer ISite.Container { get { throw new NotImplementedException(); } } bool ISite.DesignMode { get { return true; } } string ISite.Name { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } object IServiceProvider.GetService(Type serviceType) { throw new NotImplementedException(); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Controls/Test/DropDownListTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Controls.Test { public class DropDownListTest { [Fact] public void NameProperty() { // TODO: This } [Fact] public void RenderWithNoNameNotInDesignModeThrows() { // TODO: This } [Fact] public void RenderWithNoNameInDesignModeRendersWithSampleData() { // Setup DropDownList c = new DropDownList(); // Execute string html = MvcTestHelper.GetControlRendering(c, true); // Verify Assert.Equal("", html); } [Fact] public void RenderWithNoAttributes() { // Setup DropDownList c = new DropDownList(); c.Name = "nameKey"; ViewDataContainer vdc = new ViewDataContainer(); vdc.Controls.Add(c); vdc.ViewData = new ViewDataDictionary(); vdc.ViewData["nameKey"] = new SelectList(new[] { "aaa", "bbb", "ccc" }, "bbb"); // Execute string html = MvcTestHelper.GetControlRendering(c, false); // Verify Assert.Equal("", html); } [Fact] public void RenderWithTextsAndValues() { // Setup DropDownList c = new DropDownList(); c.Name = "nameKey"; ViewDataContainer vdc = new ViewDataContainer(); vdc.Controls.Add(c); vdc.ViewData = new ViewDataDictionary(); vdc.ViewData["nameKey"] = new SelectList( new[] { new { Text = "aaa", Value = "111" }, new { Text = "bbb", Value = "222" }, new { Text = "ccc", Value = "333" } }, "Value", "Text", "222"); // Execute string html = MvcTestHelper.GetControlRendering(c, false); // Verify Assert.Equal("", html); } [Fact] public void RenderWithNameAndIdRendersNameAndIdAttribute() { // Setup DropDownList c = new DropDownList(); c.Name = "nameKey"; c.ID = "someID"; ViewDataContainer vdc = new ViewDataContainer(); vdc.Controls.Add(c); vdc.ViewData = new ViewDataDictionary(); vdc.ViewData["nameKey"] = new SelectList(new[] { "aaa", "bbb", "ccc" }, "bbb"); // Execute string html = MvcTestHelper.GetControlRendering(c, false); // Verify Assert.Equal("", html); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Controls/Test/MvcControlTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using System.Web.UI; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Controls.Test { public class MvcControlTest { [Fact] public void AttributesProperty() { // Setup DummyMvcControl c = new DummyMvcControl(); // Execute IDictionary attrs = c.Attributes; // Verify Assert.NotNull(attrs); Assert.Empty(attrs); } [Fact] public void GetSetAttributes() { // Setup DummyMvcControl c = new DummyMvcControl(); IAttributeAccessor attrAccessor = c; IDictionary attrs = c.Attributes; // Execute and Verify string value; value = attrAccessor.GetAttribute("xyz"); Assert.Null(value); attrAccessor.SetAttribute("a1", "v1"); value = attrAccessor.GetAttribute("a1"); Assert.Equal("v1", value); Assert.Single(attrs); value = c.Attributes["a1"]; Assert.Equal("v1", value); } [Fact] public void EnableViewStateProperty() { DummyMvcControl c = new DummyMvcControl(); Assert.True(c.EnableViewState); Assert.True((c).EnableViewState); c.EnableViewState = false; Assert.False(c.EnableViewState); Assert.False((c).EnableViewState); c.EnableViewState = true; Assert.True(c.EnableViewState); Assert.True((c).EnableViewState); } [Fact] public void ViewContextWithNoPageIsNull() { // Setup DummyMvcControl c = new DummyMvcControl(); Control c1 = new Control(); c1.Controls.Add(c); // Execute ViewContext vc = c.ViewContext; // Verify Assert.Null(vc); } private sealed class DummyMvcControl : MvcControl { } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Controls/Test/MvcTestHelper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls.Test { public static class MvcTestHelper { public static string GetControlRendering(Control c, bool designMode) { if (designMode) { c.Site = new DesignModeSite(); } HtmlTextWriter writer = new HtmlTextWriter(new StringWriter()); c.RenderControl(writer); return writer.InnerWriter.ToString(); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Controls/Test/ViewDataContainer.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.UI; namespace Microsoft.Web.Mvc.Controls.Test { public class ViewDataContainer : Control, IViewDataContainer { public ViewDataDictionary ViewData { get; set; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Microsoft.Web.Mvc.Test.csproj ================================================  {6C28DA70-60F1-4442-967F-591BF3962EC5} Library Properties Microsoft.Web Microsoft.Web.Mvc.Test ..\..\bin\$(Configuration)\Test\ false ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll True ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True {D3CF7430-6DA4-42B0-BD90-CA39D16687B2} Microsoft.Web.Mvc {3D3FFD8A-624D-4E9B-954B-E1C105507975} System.Web.Mvc {76EFA9C5-8D7E-4FDF-B710-E20F8B6B00D2} System.Web.WebPages {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} Microsoft.TestCommon {8AC2A2E4-2F11-4D40-A887-62E2583A65E6} System.Web.Mvc.Test This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ArrayModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ArrayModelBinderProviderTest { [Fact] public void GetBinder_CorrectModelTypeAndValueProviderEntries_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; ArrayModelBinderProvider binderProvider = new ArrayModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_ModelMetadataReturnsReadOnly_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; bindingContext.ModelMetadata.IsReadOnly = true; ArrayModelBinderProvider binderProvider = new ArrayModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ModelTypeIsIncorrect_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ICollection)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; ArrayModelBinderProvider binderProvider = new ArrayModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ValueProviderDoesNotContainPrefix_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int[])), ModelName = "foo", ValueProvider = new SimpleValueProvider() }; ArrayModelBinderProvider binderProvider = new ArrayModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ArrayModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ArrayModelBinderTest { [Fact] public void BindModel() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int[])), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "84" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); // Act bool retVal = new ArrayModelBinder().BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); int[] array = bindingContext.Model as int[]; Assert.Equal(new[] { 42, 84 }, array); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/BinaryDataModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Data.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class BinaryDataModelBinderProviderTest { private static readonly byte[] _base64Bytes = new byte[] { 0x12, 0x20, 0x34, 0x40 }; private const string _base64String = "EiA0QA=="; [Fact] public void BindModel_BadValue_Fails() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(byte[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "not base64 encoded!" } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); } [Fact] public void BindModel_EmptyValue_Fails() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(byte[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "" } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); } [Fact] public void BindModel_GoodValue_ByteArray_Succeeds() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(byte[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", _base64String } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.True(retVal); Assert.Equal(_base64Bytes, (byte[])bindingContext.Model); } [Fact] public void BindModel_GoodValue_LinqBinary_Succeeds() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(Binary)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", _base64String } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.True(retVal); Binary binaryModel = Assert.IsType(bindingContext.Model); Assert.Equal(_base64Bytes, binaryModel.ToArray()); } [Fact] public void BindModel_NoValue_Fails() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(byte[])), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.bar", _base64String } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); } [Fact] public void GetBinder_WrongModelType_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", _base64String } } }; BinaryDataModelBinderProvider binderProvider = new BinaryDataModelBinderProvider(); // Act IExtensibleModelBinder modelBinder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(modelBinder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/BindingBehaviorAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class BindingBehaviorAttributeTest { [Fact] public void Behavior_Property() { // Arrange BindingBehavior expectedBehavior = (BindingBehavior)(-20); // Act BindingBehaviorAttribute attr = new BindingBehaviorAttribute(expectedBehavior); // Assert Assert.Equal(expectedBehavior, attr.Behavior); } [Fact] public void TypeId_ReturnsSameValue() { // Arrange BindNeverAttribute neverAttr = new BindNeverAttribute(); BindRequiredAttribute requiredAttr = new BindRequiredAttribute(); // Act & assert Assert.Same(neverAttr.TypeId, requiredAttr.TypeId); } [Fact] public void BindNever_SetsBehavior() { // Act BindingBehaviorAttribute attr = new BindNeverAttribute(); // Assert Assert.Equal(BindingBehavior.Never, attr.Behavior); } [Fact] public void BindRequired_SetsBehavior() { // Act BindingBehaviorAttribute attr = new BindRequiredAttribute(); // Assert Assert.Equal(BindingBehavior.Required, attr.Behavior); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/CollectionModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class CollectionModelBinderProviderTest { [Fact] public void GetBinder_CorrectModelTypeAndValueProviderEntries_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IEnumerable)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; CollectionModelBinderProvider binderProvider = new CollectionModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_ModelTypeIsIncorrect_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; CollectionModelBinderProvider binderProvider = new CollectionModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ValueProviderDoesNotContainPrefix_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IEnumerable)), ModelName = "foo", ValueProvider = new SimpleValueProvider() }; CollectionModelBinderProvider binderProvider = new CollectionModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/CollectionModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class CollectionModelBinderTest { [Fact] public void BindComplexCollectionFromIndexes_FiniteIndexes() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[foo]", "42" }, { "someName[baz]", "200" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindComplexCollectionFromIndexes(controllerContext, bindingContext, new[] { "foo", "bar", "baz" }); // Assert Assert.Equal(new[] { 42, 0, 200 }, boundCollection.ToArray()); Assert.Equal(new[] { "someName[foo]", "someName[baz]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray()); } [Fact] public void BindComplexCollectionFromIndexes_InfiniteIndexes() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[0]", "42" }, { "someName[1]", "100" }, { "someName[3]", "400" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindComplexCollectionFromIndexes(controllerContext, bindingContext, null /* indexNames */); // Assert Assert.Equal(new[] { 42, 100 }, boundCollection.ToArray()); Assert.Equal(new[] { "someName[0]", "someName[1]" }, bindingContext.ValidationNode.ChildNodes.Select(o => o.ModelStateKey).ToArray()); } [Fact] public void BindModel_ComplexCollection() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName.index", new[] { "foo", "bar", "baz" } }, { "someName[foo]", "42" }, { "someName[bar]", "100" }, { "someName[baz]", "200" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); CollectionModelBinder modelBinder = new CollectionModelBinder(); // Act bool retVal = modelBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Equal(new[] { 42, 100, 200 }, ((List)bindingContext.Model).ToArray()); } [Fact] public void BindModel_SimpleCollection() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName", new[] { "42", "100", "200" } } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); CollectionModelBinder modelBinder = new CollectionModelBinder(); // Act bool retVal = modelBinder.BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); Assert.Equal(new[] { 42, 100, 200 }, ((List)bindingContext.Model).ToArray()); } [Fact] public void BindSimpleCollection_RawValueIsEmptyCollection_ReturnsEmptyList() { // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(null, null, new object[0], null); // Assert Assert.NotNull(boundCollection); Assert.Empty(boundCollection); } [Fact] public void BindSimpleCollection_RawValueIsNull_ReturnsNull() { // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(null, null, null, null); // Assert Assert.Null(boundCollection); } [Fact] public void BindSimpleCollection_SubBinderDoesNotExist() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(controllerContext, bindingContext, new int[1], culture); // Assert Assert.Equal(new[] { 0 }, boundCollection.ToArray()); Assert.Empty(bindingContext.ValidationNode.ChildNodes); } [Fact] public void BindSimpleCollection_SubBindingSucceeds() { // Arrange ControllerContext controllerContext = new ControllerContext(); CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; ModelValidationNode childValidationNode = null; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal("someName", mbc.ModelName); childValidationNode = mbc.ValidationNode; mbc.Model = 42; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); // Act List boundCollection = CollectionModelBinder.BindSimpleCollection(controllerContext, bindingContext, new int[1], culture); // Assert Assert.Equal(new[] { 42 }, boundCollection.ToArray()); Assert.Equal(new[] { childValidationNode }, bindingContext.ValidationNode.ChildNodes.ToArray()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/CollectionModelBinderUtilTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class CollectionModelBinderUtilTest { [Fact] public void CreateOrReplaceCollection_OriginalModelImmutable_CreatesNewInstance() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => new ReadOnlyCollection(new int[0]), typeof(ICollection)) }; // Act CollectionModelBinderUtil.CreateOrReplaceCollection(bindingContext, new[] { 10, 20, 30 }, () => new List()); // Assert int[] newModel = (bindingContext.Model as ICollection).ToArray(); Assert.Equal(new[] { 10, 20, 30 }, newModel); } [Fact] public void CreateOrReplaceCollection_OriginalModelMutable_UpdatesOriginalInstance() { // Arrange List originalInstance = new List { 10, 20, 30 }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => originalInstance, typeof(ICollection)) }; // Act CollectionModelBinderUtil.CreateOrReplaceCollection(bindingContext, new[] { 40, 50, 60 }, () => new List()); // Assert Assert.Same(originalInstance, bindingContext.Model); Assert.Equal(new[] { 40, 50, 60 }, originalInstance.ToArray()); } [Fact] public void CreateOrReplaceCollection_OriginalModelNotCollection_CreatesNewInstance() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ICollection)) }; // Act CollectionModelBinderUtil.CreateOrReplaceCollection(bindingContext, new[] { 10, 20, 30 }, () => new List()); // Assert int[] newModel = (bindingContext.Model as ICollection).ToArray(); Assert.Equal(new[] { 10, 20, 30 }, newModel); } [Fact] public void CreateOrReplaceDictionary_DisallowsDuplicateKeys() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(Dictionary)) }; // Act CollectionModelBinderUtil.CreateOrReplaceDictionary( bindingContext, new[] { new KeyValuePair("forty-two", 40), new KeyValuePair("forty-two", 2), new KeyValuePair("forty-two", 42) }, () => new Dictionary()); // Assert IDictionary newModel = bindingContext.Model as IDictionary; Assert.Equal(new[] { "forty-two" }, newModel.Keys.ToArray()); Assert.Equal(42, newModel["forty-two"]); } [Fact] public void CreateOrReplaceDictionary_DisallowsNullKeys() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(Dictionary)) }; // Act CollectionModelBinderUtil.CreateOrReplaceDictionary( bindingContext, new[] { new KeyValuePair("forty-two", 42), new KeyValuePair(null, 84) }, () => new Dictionary()); // Assert IDictionary newModel = bindingContext.Model as IDictionary; Assert.Equal(new[] { "forty-two" }, newModel.Keys.ToArray()); Assert.Equal(42, newModel["forty-two"]); } [Fact] public void CreateOrReplaceDictionary_OriginalModelImmutable_CreatesNewInstance() { // Arrange ReadOnlyDictionary originalModel = new ReadOnlyDictionary(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => originalModel, typeof(IDictionary)) }; // Act CollectionModelBinderUtil.CreateOrReplaceDictionary( bindingContext, new Dictionary { { "Hello", "World" } }, () => new Dictionary()); // Assert IDictionary newModel = bindingContext.Model as IDictionary; Assert.NotSame(originalModel, newModel); Assert.Equal(new[] { "Hello" }, newModel.Keys.ToArray()); Assert.Equal("World", newModel["Hello"]); } [Fact] public void CreateOrReplaceDictionary_OriginalModelMutable_UpdatesOriginalInstance() { // Arrange Dictionary originalInstance = new Dictionary { { "dog", "Canidae" }, { "cat", "Felidae" } }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => originalInstance, typeof(IDictionary)) }; // Act CollectionModelBinderUtil.CreateOrReplaceDictionary( bindingContext, new Dictionary { { "horse", "Equidae" }, { "bear", "Ursidae" } }, () => new Dictionary()); // Assert Assert.Same(originalInstance, bindingContext.Model); Assert.Equal(new[] { "horse", "bear" }, originalInstance.Keys.ToArray()); Assert.Equal("Equidae", originalInstance["horse"]); Assert.Equal("Ursidae", originalInstance["bear"]); } [Fact] public void CreateOrReplaceDictionary_OriginalModelNotDictionary_CreatesNewInstance() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IDictionary)) }; // Act CollectionModelBinderUtil.CreateOrReplaceDictionary( bindingContext, new Dictionary { { "horse", "Equidae" }, { "bear", "Ursidae" } }, () => new Dictionary()); // Assert IDictionary newModel = bindingContext.Model as IDictionary; Assert.Equal(new[] { "horse", "bear" }, newModel.Keys.ToArray()); Assert.Equal("Equidae", newModel["horse"]); Assert.Equal("Ursidae", newModel["bear"]); } [Fact] public void GetIndexNamesFromValueProviderResult_ValueProviderResultIsNull_ReturnsNull() { // Act IEnumerable indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(null); // Assert Assert.Null(indexNames); } [Fact] public void GetIndexNamesFromValueProviderResult_ValueProviderResultReturnsEmptyArray_ReturnsNull() { // Arrange ValueProviderResult vpResult = new ValueProviderResult(new string[0], "", null); // Act IEnumerable indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(vpResult); // Assert Assert.Null(indexNames); } [Fact] public void GetIndexNamesFromValueProviderResult_ValueProviderResultReturnsNonEmptyArray_ReturnsArray() { // Arrange ValueProviderResult vpResult = new ValueProviderResult(new[] { "foo", "bar", "baz" }, "foo,bar,baz", null); // Act IEnumerable indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(vpResult); // Assert Assert.NotNull(indexNames); Assert.Equal(new[] { "foo", "bar", "baz" }, indexNames.ToArray()); } [Fact] public void GetIndexNamesFromValueProviderResult_ValueProviderResultReturnsNull_ReturnsNull() { // Arrange ValueProviderResult vpResult = new ValueProviderResult(null, null, null); // Act IEnumerable indexNames = CollectionModelBinderUtil.GetIndexNamesFromValueProviderResult(vpResult); // Assert Assert.Null(indexNames); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ModelTypeNotGeneric_Fail() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)); // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(null, null, modelMetadata); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ModelTypeOpenGeneric_Fail() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IList<>)); // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(null, null, modelMetadata); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ModelTypeWrongNumberOfGenericArguments_Fail() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)); // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(ICollection<>), null, modelMetadata); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadOnlyReference_ModelInstanceImmutable_Valid() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => new int[0], typeof(IList)); modelMetadata.IsReadOnly = true; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(IList<>), typeof(List<>), modelMetadata); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadOnlyReference_ModelInstanceMutable_Valid() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => new List(), typeof(IList)); modelMetadata.IsReadOnly = true; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(IList<>), typeof(List<>), modelMetadata); // Assert Assert.Equal(new[] { typeof(int) }, typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadOnlyReference_ModelInstanceOfWrongType_Fail() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => new HashSet(), typeof(ICollection)); modelMetadata.IsReadOnly = true; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(IList<>), typeof(List<>), modelMetadata); // Assert // HashSet<> is not an IList<>, so we can't update Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadOnlyReference_ModelIsNull_Fail() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IList)); modelMetadata.IsReadOnly = true; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(ICollection<>), typeof(List<>), modelMetadata); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadWriteReference_NewInstanceAssignableToModelType_Success() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IList)); modelMetadata.IsReadOnly = false; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(ICollection<>), typeof(List<>), modelMetadata); // Assert Assert.Equal(new[] { typeof(int) }, typeArguments); } [Fact] public void GetTypeArgumentsForUpdatableGenericCollection_ReadWriteReference_NewInstanceNotAssignableToModelType_MutableInstance_Success() { // Arrange ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => new Collection(), typeof(Collection)); modelMetadata.IsReadOnly = false; // Act Type[] typeArguments = CollectionModelBinderUtil.GetTypeArgumentsForUpdatableGenericCollection(typeof(ICollection<>), typeof(List<>), modelMetadata); // Assert Assert.Equal(new[] { typeof(int) }, typeArguments); } [Fact] public void GetZeroBasedIndexes() { // Act string[] indexes = CollectionModelBinderUtil.GetZeroBasedIndexes().Take(5).ToArray(); // Assert Assert.Equal(new[] { "0", "1", "2", "3", "4" }, indexes); } private class ReadOnlyDictionary : Dictionary, ICollection> { bool ICollection>.IsReadOnly { get { return true; } } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ComplexModelDtoModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ComplexModelDtoModelBinderProviderTest { [Fact] public void GetBinder_TypeDoesNotMatch_ReturnsNull() { // Arrange ComplexModelDtoModelBinderProvider provider = new ComplexModelDtoModelBinderProvider(); ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(object)); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_TypeMatches_ReturnsBinder() { // Arrange ComplexModelDtoModelBinderProvider provider = new ComplexModelDtoModelBinderProvider(); ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(ComplexModelDto)); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.IsType(binder); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => null, modelType) }; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ComplexModelDtoModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ComplexModelDtoModelBinderTest { [Fact] public void BindModel() { // Arrange ControllerContext controllerContext = new ControllerContext(); MyModel model = new MyModel(); ModelMetadata modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => model, typeof(MyModel)); ComplexModelDto dto = new ComplexModelDto(modelMetadata, modelMetadata.Properties); Mock mockStringBinder = new Mock(); mockStringBinder .Setup(b => b.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal(typeof(string), mbc.ModelType); Assert.Equal("theModel.StringProperty", mbc.ModelName); mbc.ValidationNode = new ModelValidationNode(mbc.ModelMetadata, "theModel.StringProperty"); mbc.Model = "someStringValue"; return true; }); Mock mockIntBinder = new Mock(); mockIntBinder .Setup(b => b.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal(typeof(int), mbc.ModelType); Assert.Equal("theModel.IntProperty", mbc.ModelName); mbc.ValidationNode = new ModelValidationNode(mbc.ModelMetadata, "theModel.IntProperty"); mbc.Model = 42; return true; }); Mock mockDateTimeBinder = new Mock(); mockDateTimeBinder .Setup(b => b.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal(typeof(DateTime), mbc.ModelType); Assert.Equal("theModel.DateTimeProperty", mbc.ModelName); return false; }); ModelBinderProviderCollection binders = new ModelBinderProviderCollection(); binders.RegisterBinderForType(typeof(string), mockStringBinder.Object, true /* suppressPrefixCheck */); binders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); binders.RegisterBinderForType(typeof(DateTime), mockDateTimeBinder.Object, true /* suppressPrefixCheck */); ExtensibleModelBindingContext parentBindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => dto, typeof(ComplexModelDto)), ModelName = "theModel", ModelBinderProviders = binders }; ComplexModelDtoModelBinder binder = new ComplexModelDtoModelBinder(); // Act bool retVal = binder.BindModel(controllerContext, parentBindingContext); // Assert Assert.True(retVal); Assert.Equal(dto, parentBindingContext.Model); ComplexModelDtoResult stringDtoResult = dto.Results[dto.PropertyMetadata.Where(m => m.ModelType == typeof(string)).First()]; Assert.Equal("someStringValue", stringDtoResult.Model); Assert.Equal("theModel.StringProperty", stringDtoResult.ValidationNode.ModelStateKey); ComplexModelDtoResult intDtoResult = dto.Results[dto.PropertyMetadata.Where(m => m.ModelType == typeof(int)).First()]; Assert.Equal(42, intDtoResult.Model); Assert.Equal("theModel.IntProperty", intDtoResult.ValidationNode.ModelStateKey); ComplexModelDtoResult dateTimeDtoResult = dto.Results[dto.PropertyMetadata.Where(m => m.ModelType == typeof(DateTime)).First()]; Assert.Null(dateTimeDtoResult); } private static ModelBindingContext GetBindingContext(Type modelType) { return new ModelBindingContext { ModelMetadata = new ModelMetadata(new Mock().Object, null, null, modelType, "SomeProperty") }; } private sealed class MyModel { public string StringProperty { get; set; } public int IntProperty { get; set; } public object ObjectProperty { get; set; } // no binding should happen since no registered binder public DateTime DateTimeProperty { get; set; } // registered binder returns false } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ComplexModelDtoResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ComplexModelDtoResultTest { [Fact] public void Constructor_ThrowsIfValidationNodeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new ComplexModelDtoResult("some string", null); }, "validationNode"); } [Fact] public void Constructor_SetsProperties() { // Arrange ModelValidationNode validationNode = GetValidationNode(); // Act ComplexModelDtoResult result = new ComplexModelDtoResult("some string", validationNode); // Assert Assert.Equal("some string", result.Model); Assert.Equal(validationNode, result.ValidationNode); } private static ModelValidationNode GetValidationNode() { EmptyModelMetadataProvider provider = new EmptyModelMetadataProvider(); ModelMetadata metadata = provider.GetMetadataForType(null, typeof(object)); return new ModelValidationNode(metadata, "someKey"); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ComplexModelDtoTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ComplexModelDtoTest { [Fact] public void ConstructorThrowsIfModelMetadataIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new ComplexModelDto(null, Enumerable.Empty()); }, "modelMetadata"); } [Fact] public void ConstructorThrowsIfPropertyMetadataIsNull() { // Arrange ModelMetadata modelMetadata = GetModelMetadata(); // Act & assert Assert.ThrowsArgumentNull( delegate { new ComplexModelDto(modelMetadata, null); }, "propertyMetadata"); } [Fact] public void ConstructorSetsProperties() { // Arrange ModelMetadata modelMetadata = GetModelMetadata(); ModelMetadata[] propertyMetadata = new ModelMetadata[0]; // Act ComplexModelDto dto = new ComplexModelDto(modelMetadata, propertyMetadata); // Assert Assert.Equal(modelMetadata, dto.ModelMetadata); Assert.Equal(propertyMetadata, dto.PropertyMetadata.ToArray()); Assert.Empty(dto.Results); } private static ModelMetadata GetModelMetadata() { return new ModelMetadata(new EmptyModelMetadataProvider(), typeof(object), null, typeof(object), "PropertyName"); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/DictionaryModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class DictionaryModelBinderProviderTest { [Fact] public void GetBinder_CorrectModelTypeAndValueProviderEntries_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IDictionary)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; DictionaryModelBinderProvider binderProvider = new DictionaryModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_ModelTypeIsIncorrect_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo[0]", "42" }, } }; DictionaryModelBinderProvider binderProvider = new DictionaryModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ValueProviderDoesNotContainPrefix_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IDictionary)), ModelName = "foo", ValueProvider = new SimpleValueProvider() }; DictionaryModelBinderProvider binderProvider = new DictionaryModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/DictionaryModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class DictionaryModelBinderTest { [Fact] public void BindModel() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(IDictionary)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider { { "someName[0]", new KeyValuePair(42, "forty-two") }, { "someName[1]", new KeyValuePair(84, "eighty-four") } } }; Mock mockKvpBinder = new Mock(); mockKvpBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = mbc.ValueProvider.GetValue(mbc.ModelName).ConvertTo(mbc.ModelType); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(KeyValuePair), mockKvpBinder.Object, false /* suppressPrefixCheck */); // Act bool retVal = new DictionaryModelBinder().BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); var dictionary = Assert.IsAssignableFrom>(bindingContext.Model); Assert.NotNull(dictionary); Assert.Equal(2, dictionary.Count); Assert.Equal("forty-two", dictionary[42]); Assert.Equal("eighty-four", dictionary[84]); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ExtensibleModelBinderAdapterTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ExtensibleModelBinderAdapterTest { [Fact] public void BindModel_PropertyFilterIsSet_Throws() { // Arrange ControllerContext controllerContext = GetControllerContext(); ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = true, ModelMetadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(SimpleModel)), PropertyFilter = (new BindAttribute { Include = "FirstName " }).IsPropertyAllowed }; ModelBinderProviderCollection binderProviders = new ModelBinderProviderCollection(); ExtensibleModelBinderAdapter shimBinder = new ExtensibleModelBinderAdapter(binderProviders); // Act & assert Assert.Throws( delegate { shimBinder.BindModel(controllerContext, bindingContext); }, @"The new model binding system cannot be used when a property whitelist or blacklist has been specified in [Bind] or via the call to UpdateModel() / TryUpdateModel(). Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead."); } [Fact] public void BindModel_SuccessfulBind_RunsValidationAndReturnsModel() { // Arrange ControllerContext controllerContext = GetControllerContext(); bool validationCalled = false; ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = true, ModelMetadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelState = controllerContext.Controller.ViewData.ModelState, PropertyFilter = _ => true, ValueProvider = new SimpleValueProvider { { "someName", "dummyValue" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Same(bindingContext.ModelMetadata, mbc.ModelMetadata); Assert.Equal("someName", mbc.ModelName); Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider); mbc.Model = 42; mbc.ValidationNode.Validating += delegate { validationCalled = true; }; return true; }); ModelBinderProviderCollection binderProviders = new ModelBinderProviderCollection(); binderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, false /* suppressPrefixCheck */); ExtensibleModelBinderAdapter shimBinder = new ExtensibleModelBinderAdapter(binderProviders); // Act object retVal = shimBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Equal(42, retVal); Assert.True(validationCalled); Assert.True(bindingContext.ModelState.IsValid); } [Fact] public void BindModel_SuccessfulBind_ComplexTypeFallback_RunsValidationAndReturnsModel() { // Arrange ControllerContext controllerContext = GetControllerContext(); bool validationCalled = false; List expectedModel = new List { 1, 2, 3, 4, 5 }; ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = true, ModelMetadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(List)), ModelName = "someName", ModelState = controllerContext.Controller.ViewData.ModelState, PropertyFilter = _ => true, ValueProvider = new SimpleValueProvider { { "someOtherName", "dummyValue" } } }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Same(bindingContext.ModelMetadata, mbc.ModelMetadata); Assert.Equal("", mbc.ModelName); Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider); mbc.Model = expectedModel; mbc.ValidationNode.Validating += delegate { validationCalled = true; }; return true; }); ModelBinderProviderCollection binderProviders = new ModelBinderProviderCollection(); binderProviders.RegisterBinderForType(typeof(List), mockIntBinder.Object, false /* suppressPrefixCheck */); ExtensibleModelBinderAdapter shimBinder = new ExtensibleModelBinderAdapter(binderProviders); // Act object retVal = shimBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Equal(expectedModel, retVal); Assert.True(validationCalled); Assert.True(bindingContext.ModelState.IsValid); } [Fact] public void BindModel_UnsuccessfulBind_BinderFails_ReturnsNull() { // Arrange ControllerContext controllerContext = GetControllerContext(); Mock mockListBinder = new Mock(); mockListBinder.Setup(o => o.BindModel(controllerContext, It.IsAny())).Returns(false).Verifiable(); ModelBinderProviderCollection binderProviders = new ModelBinderProviderCollection(); binderProviders.RegisterBinderForType(typeof(List), mockListBinder.Object, true /* suppressPrefixCheck */); ExtensibleModelBinderAdapter shimBinder = new ExtensibleModelBinderAdapter(binderProviders); ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = false, ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List)), ModelState = controllerContext.Controller.ViewData.ModelState }; // Act object retVal = shimBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Null(retVal); Assert.True(bindingContext.ModelState.IsValid); mockListBinder.Verify(); } [Fact] public void BindModel_UnsuccessfulBind_SimpleTypeNoFallback_ReturnsNull() { // Arrange ControllerContext controllerContext = GetControllerContext(); Mock mockBinderProvider = new Mock(); mockBinderProvider.Setup(o => o.GetBinder(controllerContext, It.IsAny())).Returns((IExtensibleModelBinder)null).Verifiable(); ModelBinderProviderCollection binderProviders = new ModelBinderProviderCollection { mockBinderProvider.Object }; ExtensibleModelBinderAdapter shimBinder = new ExtensibleModelBinderAdapter(binderProviders); ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = true, ModelMetadata = new DataAnnotationsModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelState = controllerContext.Controller.ViewData.ModelState }; // Act object retVal = shimBinder.BindModel(controllerContext, bindingContext); // Assert Assert.Null(retVal); Assert.True(bindingContext.ModelState.IsValid); mockBinderProvider.Verify(); mockBinderProvider.Verify(o => o.GetBinder(controllerContext, It.IsAny()), Times.AtMostOnce()); } private static ControllerContext GetControllerContext() { return new ControllerContext { Controller = new SimpleController() }; } private class SimpleController : Controller { } private class SimpleModel { public string FirstName { get; set; } public string LastName { get; set; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ExtensibleModelBindingContextTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using System.Web.TestUtil; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ExtensibleModelBindingContextTest { [Fact] public void CopyConstructor() { // Arrange ExtensibleModelBindingContext originalBindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)), ModelName = "theName", ModelState = new ModelStateDictionary(), ValueProvider = new SimpleValueProvider() }; // Act ExtensibleModelBindingContext newBindingContext = new ExtensibleModelBindingContext(originalBindingContext); // Assert Assert.Null(newBindingContext.ModelMetadata); Assert.Equal("", newBindingContext.ModelName); Assert.Equal(originalBindingContext.ModelState, newBindingContext.ModelState); Assert.Equal(originalBindingContext.ValueProvider, newBindingContext.ValueProvider); } [Fact] public void ModelBinderProvidersProperty() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext(); // Act & assert MemberHelper.TestPropertyWithDefaultInstance(bindingContext, "ModelBinderProviders", new ModelBinderProviderCollection(), ModelBinderProviders.Providers); } [Fact] public void ModelProperty() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)) }; // Act & assert MemberHelper.TestPropertyValue(bindingContext, "Model", 42); } [Fact] public void ModelProperty_ThrowsIfModelMetadataDoesNotExist() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext(); // Act & assert Assert.Throws( delegate { bindingContext.Model = null; }, "The ModelMetadata property must be set before accessing this property."); } [Fact] public void ModelNameProperty() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext(); // Act & assert Assert.Reflection.StringProperty(bindingContext, (context) => context.ModelName, String.Empty); } [Fact] public void ModelStateProperty() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext(); ModelStateDictionary modelState = new ModelStateDictionary(); // Act & assert MemberHelper.TestPropertyWithDefaultInstance(bindingContext, "ModelState", modelState); } [Fact] public void ModelAndModelTypeAreFedFromModelMetadata() { // Act ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => 42, typeof(int)) }; // Assert Assert.Equal(42, bindingContext.Model); Assert.Equal(typeof(int), bindingContext.ModelType); } [Fact] public void ValidationNodeProperty() { // Act ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => 42, typeof(int)) }; // Act & assert MemberHelper.TestPropertyWithDefaultInstance(bindingContext, "ValidationNode", new ModelValidationNode(bindingContext.ModelMetadata, "someName")); } [Fact] public void ValidationNodeProperty_DefaultValues() { // Act ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => 42, typeof(int)), ModelName = "theInt" }; // Act ModelValidationNode validationNode = bindingContext.ValidationNode; // Assert Assert.NotNull(validationNode); Assert.Equal(bindingContext.ModelMetadata, validationNode.ModelMetadata); Assert.Equal(bindingContext.ModelName, validationNode.ModelStateKey); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/GenericModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class GenericModelBinderProviderTest { [Fact] public void Constructor_WithFactory_ThrowsIfModelBinderFactoryIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(typeof(List<>), (Func)null); }, "modelBinderFactory"); } [Fact] public void Constructor_WithFactory_ThrowsIfModelTypeIsNotOpenGeneric() { // Act & assert Assert.Throws( delegate { new GenericModelBinderProvider(typeof(List), _ => null); }, "The type 'System.Collections.Generic.List`1[System.Int32]' is not an open generic type." + Environment.NewLine + "Parameter name: modelType"); } [Fact] public void Constructor_WithFactory_ThrowsIfModelTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(null, _ => null); }, "modelType"); } [Fact] public void Constructor_WithInstance_ThrowsIfModelBinderIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(typeof(List<>), (IExtensibleModelBinder)null); }, "modelBinder"); } [Fact] public void Constructor_WithInstance_ThrowsIfModelTypeIsNotOpenGeneric() { // Act & assert Assert.Throws( delegate { new GenericModelBinderProvider(typeof(List), new MutableObjectModelBinder()); }, "The type 'System.Collections.Generic.List`1[System.Int32]' is not an open generic type." + Environment.NewLine + "Parameter name: modelType"); } [Fact] public void Constructor_WithInstance_ThrowsIfModelTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(null, new MutableObjectModelBinder()); }, "modelType"); } [Fact] public void Constructor_WithType_ThrowsIfModelBinderTypeIsNotModelBinder() { // Act & assert Assert.Throws( delegate { new GenericModelBinderProvider(typeof(List<>), typeof(string)); }, "The type 'System.String' does not implement the interface 'Microsoft.Web.Mvc.ModelBinding.IExtensibleModelBinder'." + Environment.NewLine + "Parameter name: modelBinderType"); } [Fact] public void Constructor_WithType_ThrowsIfModelBinderTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(typeof(List<>), (Type)null); }, "modelBinderType"); } [Fact] public void Constructor_WithType_ThrowsIfModelBinderTypeTypeArgumentMismatch() { // Act & assert Assert.Throws( delegate { new GenericModelBinderProvider(typeof(List<>), typeof(DictionaryModelBinder<,>)); }, "The open model type 'System.Collections.Generic.List`1[T]' has 1 generic type argument(s), but the open binder type 'Microsoft.Web.Mvc.ModelBinding.DictionaryModelBinder`2[TKey,TValue]' has 2 generic type argument(s). The binder type must not be an open generic type or must have the same number of generic arguments as the open model type." + Environment.NewLine + "Parameter name: modelBinderType"); } [Fact] public void Constructor_WithType_ThrowsIfModelTypeIsNotOpenGeneric() { // Act & assert Assert.Throws( delegate { new GenericModelBinderProvider(typeof(List), typeof(MutableObjectModelBinder)); }, "The type 'System.Collections.Generic.List`1[System.Int32]' is not an open generic type." + Environment.NewLine + "Parameter name: modelType"); } [Fact] public void Constructor_WithType_ThrowsIfModelTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new GenericModelBinderProvider(null, typeof(MutableObjectModelBinder)); }, "modelType"); } [Fact] public void GetBinder_TypeDoesNotMatch_ModelTypeIsInterface_ReturnsNull() { // Arrange GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(IEnumerable<>), typeof(CollectionModelBinder<>)) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(object)); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_TypeDoesNotMatch_ModelTypeIsNotInterface_ReturnsNull() { // Arrange GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(List<>), typeof(CollectionModelBinder<>)) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(object)); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_TypeMatches_PrefixNotFound_ReturnsNull() { // Arrange IExtensibleModelBinder binderInstance = new Mock().Object; GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(List<>), binderInstance); ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(List)); bindingContext.ValueProvider = new SimpleValueProvider(); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(returnedBinder); } [Fact] public void GetBinder_TypeMatches_Success_Factory_ReturnsBinder() { // Arrange IExtensibleModelBinder binderInstance = new Mock().Object; Func binderFactory = typeArguments => { Assert.Equal(new[] { typeof(int) }, typeArguments); return binderInstance; }; GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(IList<>), binderFactory) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(List)); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Same(binderInstance, returnedBinder); } [Fact] public void GetBinder_TypeMatches_Success_Instance_ReturnsBinder() { // Arrange IExtensibleModelBinder binderInstance = new Mock().Object; GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(List<>), binderInstance) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(List)); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Same(binderInstance, returnedBinder); } [Fact] public void GetBinder_TypeMatches_Success_TypeActivation_ReturnsBinder() { // Arrange GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(List<>), typeof(CollectionModelBinder<>)) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(List)); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.IsType>(returnedBinder); } [Fact] public void GetBinderThrowsIfBindingContextIsNull() { // Arrange GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(IEnumerable<>), typeof(CollectionModelBinder<>)); // Act & assert Assert.ThrowsArgumentNull( delegate { provider.GetBinder(null, null); }, "bindingContext"); } [Fact] public void GetBinderThrowsIfModelBinderHasNoParameterlessConstructor() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(List)); GenericModelBinderProvider provider = new GenericModelBinderProvider(typeof(List<>), typeof(NoParameterlessCtorBinder<>)) { SuppressPrefixCheck = true, }; // Act & Assert, confirming type name and full stack are available in Exception MissingMethodException exception = Assert.Throws( () => provider.GetBinder(null, bindingContext), "No parameterless constructor defined for this object. Object type 'Microsoft.Web.Mvc.ModelBinding.Test.GenericModelBinderProviderTest+NoParameterlessCtorBinder`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'."); Assert.Contains("System.Activator.CreateInstance(", exception.ToString()); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => null, modelType) }; } private class NoParameterlessCtorBinder : IExtensibleModelBinder { public NoParameterlessCtorBinder(int parameter) { } public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/KeyValuePairModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class KeyValuePairModelBinderProviderTest { [Fact] public void GetBinder_CorrectModelTypeAndValueProviderEntries_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.key", 42 }, { "foo.value", "someValue" } } }; KeyValuePairModelBinderProvider binderProvider = new KeyValuePairModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_ModelTypeIsIncorrect_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(List)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.key", 42 }, { "foo.value", "someValue" } } }; KeyValuePairModelBinderProvider binderProvider = new KeyValuePairModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ValueProviderDoesNotContainKeyProperty_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.value", "someValue" } } }; KeyValuePairModelBinderProvider binderProvider = new KeyValuePairModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_ValueProviderDoesNotContainValueProperty_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.key", 42 } } }; KeyValuePairModelBinderProvider binderProvider = new KeyValuePairModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/KeyValuePairModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class KeyValuePairModelBinderTest { [Fact] public void BindModel_MissingKey_ReturnsFalse() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; KeyValuePairModelBinder binder = new KeyValuePairModelBinder(); // Act bool retVal = binder.BindModel(controllerContext, bindingContext); // Assert Assert.False(retVal); Assert.Null(bindingContext.Model); Assert.Empty(bindingContext.ValidationNode.ChildNodes); } [Fact] public void BindModel_MissingValue_ReturnsTrue() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = 42; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); KeyValuePairModelBinder binder = new KeyValuePairModelBinder(); // Act bool retVal = binder.BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); Assert.Null(bindingContext.Model); Assert.Equal(new[] { "someName.key" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray()); } [Fact] public void BindModel_SubBindingSucceeds() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(KeyValuePair)), ModelName = "someName", ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = 42; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); Mock mockStringBinder = new Mock(); mockStringBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { mbc.Model = "forty-two"; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(string), mockStringBinder.Object, true /* suppressPrefixCheck */); KeyValuePairModelBinder binder = new KeyValuePairModelBinder(); // Act bool retVal = binder.BindModel(controllerContext, bindingContext); // Assert Assert.True(retVal); Assert.Equal(new KeyValuePair(42, "forty-two"), bindingContext.Model); Assert.Equal(new[] { "someName.key", "someName.value" }, bindingContext.ValidationNode.ChildNodes.Select(n => n.ModelStateKey).ToArray()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/KeyValuePairModelBinderUtilTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class KeyValuePairModelBinderUtilTest { [Fact] public void TryBindStrongModel_BinderExists_BinderReturnsCorrectlyTypedObject_ReturnsTrue() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelState = new ModelStateDictionary(), ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal("someName.key", mbc.ModelName); mbc.Model = 42; return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); // Act int model; bool retVal = KeyValuePairModelBinderUtil.TryBindStrongModel(controllerContext, bindingContext, "key", new EmptyModelMetadataProvider(), out model); // Assert Assert.True(retVal); Assert.Equal(42, model); Assert.Single(bindingContext.ValidationNode.ChildNodes); Assert.Empty(bindingContext.ModelState); } [Fact] public void TryBindStrongModel_BinderExists_BinderReturnsIncorrectlyTypedObject_ReturnsTrue() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelState = new ModelStateDictionary(), ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; Mock mockIntBinder = new Mock(); mockIntBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc) { Assert.Equal("someName.key", mbc.ModelName); return true; }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(int), mockIntBinder.Object, true /* suppressPrefixCheck */); // Act int model; bool retVal = KeyValuePairModelBinderUtil.TryBindStrongModel(controllerContext, bindingContext, "key", new EmptyModelMetadataProvider(), out model); // Assert Assert.True(retVal); Assert.Equal(default(int), model); Assert.Single(bindingContext.ValidationNode.ChildNodes); Assert.Empty(bindingContext.ModelState); } [Fact] public void TryBindStrongModel_NoBinder_ReturnsFalse() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)), ModelName = "someName", ModelState = new ModelStateDictionary(), ModelBinderProviders = new ModelBinderProviderCollection(), ValueProvider = new SimpleValueProvider() }; // Act int model; bool retVal = KeyValuePairModelBinderUtil.TryBindStrongModel(controllerContext, bindingContext, "key", new EmptyModelMetadataProvider(), out model); // Assert Assert.False(retVal); Assert.Equal(default(int), model); Assert.Empty(bindingContext.ValidationNode.ChildNodes); Assert.Empty(bindingContext.ModelState); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ModelBinderConfigTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web; using System.Web.Mvc; using System.Web.TestUtil; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelBinderConfigTest { [Fact] public void GetUserResourceString_NullControllerContext_ReturnsNull() { // Act string customResourceString = ModelBinderConfig.GetUserResourceString(null /* controllerContext */, "someResourceName", "someResourceClassKey"); // Assert Assert.Null(customResourceString); } [Fact] public void GetUserResourceString_NullHttpContext_ReturnsNull() { Mock mockControllerContext = new Mock(); mockControllerContext.Setup(o => o.HttpContext).Returns((HttpContextBase)null); // Act string customResourceString = ModelBinderConfig.GetUserResourceString(mockControllerContext.Object, "someResourceName", "someResourceClassKey"); // Assert Assert.Null(customResourceString); } [Fact] public void GetUserResourceString_NullResourceKey_ReturnsNull() { Mock mockControllerContext = new Mock(); // Act string customResourceString = ModelBinderConfig.GetUserResourceString(mockControllerContext.Object, "someResourceName", null /* resourceClassKey */); // Assert mockControllerContext.Verify(o => o.HttpContext, Times.Never()); Assert.Null(customResourceString); } [Fact] public void GetUserResourceString_ValidResourceObject_ReturnsResourceString() { Mock mockControllerContext = new Mock(); mockControllerContext.Setup(o => o.HttpContext.GetGlobalResourceObject("someResourceClassKey", "someResourceName", CultureInfo.CurrentUICulture)).Returns("My custom resource string"); // Act string customResourceString = ModelBinderConfig.GetUserResourceString(mockControllerContext.Object, "someResourceName", "someResourceClassKey"); // Assert Assert.Equal("My custom resource string", customResourceString); } [Fact] public void Initialize_ReplacesOriginalCollection() { // Arrange ModelBinderDictionary oldBinders = new ModelBinderDictionary(); oldBinders[typeof(int)] = new Mock().Object; ModelBinderProviderCollection newBinderProviders = new ModelBinderProviderCollection(); // Act ModelBinderConfig.Initialize(oldBinders, newBinderProviders); // Assert Assert.Empty(oldBinders); var shimBinder = Assert.IsType(oldBinders.DefaultBinder); Assert.Same(newBinderProviders, shimBinder.Providers); } [Fact] public void TypeConversionErrorMessageProvider_DefaultValue() { // Arrange ModelMetadata metadata = new ModelMetadata(new Mock().Object, null, null, typeof(int), "SomePropertyName"); // Act string errorString = ModelBinderConfig.TypeConversionErrorMessageProvider(null, metadata, "some incoming value"); // Assert Assert.Equal("The value 'some incoming value' is not valid for SomePropertyName.", errorString); } [Fact] public void TypeConversionErrorMessageProvider_Property() { // Arrange ModelBinderConfigWrapper wrapper = new ModelBinderConfigWrapper(); // Act & assert try { MemberHelper.TestPropertyWithDefaultInstance(wrapper, "TypeConversionErrorMessageProvider", (ModelBinderErrorMessageProvider)DummyErrorSelector); } finally { wrapper.Reset(); } } [Fact] public void ValueRequiredErrorMessageProvider_DefaultValue() { // Arrange ModelMetadata metadata = new ModelMetadata(new Mock().Object, null, null, typeof(int), "SomePropertyName"); // Act string errorString = ModelBinderConfig.ValueRequiredErrorMessageProvider(null, metadata, "some incoming value"); // Assert Assert.Equal("A value is required.", errorString); } [Fact] public void ValueRequiredErrorMessageProvider_Property() { // Arrange ModelBinderConfigWrapper wrapper = new ModelBinderConfigWrapper(); // Act & assert try { MemberHelper.TestPropertyWithDefaultInstance(wrapper, "ValueRequiredErrorMessageProvider", (ModelBinderErrorMessageProvider)DummyErrorSelector); } finally { wrapper.Reset(); } } private string DummyErrorSelector(ControllerContext controllerContext, ModelMetadata modelMetadata, object incomingValue) { throw new NotImplementedException(); } private sealed class ModelBinderConfigWrapper { public ModelBinderErrorMessageProvider TypeConversionErrorMessageProvider { get { return ModelBinderConfig.TypeConversionErrorMessageProvider; } set { ModelBinderConfig.TypeConversionErrorMessageProvider = value; } } public ModelBinderErrorMessageProvider ValueRequiredErrorMessageProvider { get { return ModelBinderConfig.ValueRequiredErrorMessageProvider; } set { ModelBinderConfig.ValueRequiredErrorMessageProvider = value; } } public void Reset() { ModelBinderConfig.TypeConversionErrorMessageProvider = null; ModelBinderConfig.ValueRequiredErrorMessageProvider = null; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ModelBinderProviderCollectionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelBinderProviderCollectionTest { [Fact] public void ListWrappingConstructor() { // Arrange ModelBinderProvider[] providers = new[] { new Mock().Object, new Mock().Object }; // Act ModelBinderProviderCollection collection = new ModelBinderProviderCollection(providers); // Assert Assert.Equal(providers, collection.ToArray()); } [Fact] public void DefaultConstructor() { // Act ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Assert Assert.Empty(collection); } [Fact] public void AddNullProviderThrows() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & Assert Assert.ThrowsArgumentNull( delegate { collection.Add(null); }, "item"); } [Fact] public void RegisterBinderForGenericType_Factory() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), _ => mockBinder); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForGenericType_Instance() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), mockBinder); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForGenericType_Type() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForGenericType(typeof(List<>), typeof(CollectionModelBinder<>)); // Assert var genericProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(List<>), genericProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Factory() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForType(typeof(int), () => mockBinder); // Assert var simpleProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(int), simpleProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Instance() { // Arrange ModelBinderProvider mockProvider = new Mock().Object; IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { mockProvider }; // Act collection.RegisterBinderForType(typeof(int), mockBinder); // Assert var simpleProvider = Assert.IsType(collection[0]); Assert.Equal(typeof(int), simpleProvider.ModelType); Assert.Equal(mockProvider, collection[1]); } [Fact] public void RegisterBinderForType_Instance_InsertsNewProviderBehindFrontOfListProviders() { // Arrange ModelBinderProvider frontOfListProvider = new ProviderAtFront(); IExtensibleModelBinder mockBinder = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection { frontOfListProvider }; // Act collection.RegisterBinderForType(typeof(int), mockBinder); // Assert Assert.Equal( new[] { typeof(ProviderAtFront), typeof(SimpleModelBinderProvider) }, collection.Select(o => o.GetType()).ToArray()); } [Fact] public void SetItem() { // Arrange ModelBinderProvider provider0 = new Mock().Object; ModelBinderProvider provider1 = new Mock().Object; ModelBinderProvider provider2 = new Mock().Object; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); collection.Add(provider0); collection.Add(provider1); // Act collection[1] = provider2; // Assert Assert.Equal(new[] { provider0, provider2 }, collection.ToArray()); } [Fact] public void SetNullProviderThrows() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); collection.Add(new Mock().Object); // Act & Assert Assert.ThrowsArgumentNull( delegate { collection[0] = null; }, "item"); } [Fact] public void GetBinder_FromAttribute_BadAttribute_Throws() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_BadAttribute)) }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); // Act & assert Assert.Throws( delegate { providers.GetBinder(controllerContext, bindingContext); }, @"The type 'System.Object' does not subclass Microsoft.Web.Mvc.ModelBinding.ModelBinderProvider or implement the interface Microsoft.Web.Mvc.ModelBinding.IExtensibleModelBinder."); } [Fact] public void GetBinder_FromAttribute_Binder_Generic_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder_Generic)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "fooValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder_Generic), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType>(binder); } [Fact] public void GetBinder_FromAttribute_Binder_SuppressPrefixCheck_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder_SuppressPrefix)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "bar", "barValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder_SuppressPrefix), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinder_FromAttribute_Binder_ValueNotPresent_ReturnsNull() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "bar", "barValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_FromAttribute_Binder_ValuePresent_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Binder)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo", "fooValue" } } }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Binder), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinder_FromAttribute_Provider_ReturnsBinder() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithProviderAttribute_Provider)) }; ModelBinderProviderCollection providers = new ModelBinderProviderCollection(); providers.RegisterBinderForType(typeof(ModelWithProviderAttribute_Provider), new Mock().Object, true /* suppressPrefix */); // Act IExtensibleModelBinder binder = providers.GetBinder(controllerContext, bindingContext); // Assert Assert.IsType(binder); } [Fact] public void GetBinderReturnsFirstBinderFromProviders() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)) }; IExtensibleModelBinder expectedBinder = new Mock().Object; Mock mockProvider = new Mock(); mockProvider.Setup(p => p.GetBinder(controllerContext, bindingContext)).Returns(expectedBinder); ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, mockProvider.Object, new Mock().Object }); // Act IExtensibleModelBinder returned = collection.GetBinder(controllerContext, bindingContext); // Assert Assert.Equal(expectedBinder, returned); } [Fact] public void GetBinderReturnsNullIfNoProviderMatches() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, }); // Act IExtensibleModelBinder returned = collection.GetBinder(controllerContext, bindingContext); // Assert Assert.Null(returned); } [Fact] public void GetBinderThrowsIfBindingContextIsNull() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.ThrowsArgumentNull( delegate { collection.GetBinder(new ControllerContext(), null); }, "bindingContext"); } [Fact] public void GetBinderThrowsIfControllerContextIsNull() { // Arrange ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.ThrowsArgumentNull( delegate { collection.GetBinder(null, new ExtensibleModelBindingContext()); }, "controllerContext"); } [Fact] public void GetBinderThrowsIfModelTypeHasBindAttribute() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ModelWithBindAttribute)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); // Act & assert Assert.Throws( delegate { collection.GetBinder(controllerContext, bindingContext); }, @"The model of type 'Microsoft.Web.Mvc.ModelBinding.Test.ModelBinderProviderCollectionTest+ModelWithBindAttribute' has a [Bind] attribute. The new model binding system cannot be used with models that have type-level [Bind] attributes. Use the [BindRequired] and [BindNever] attributes on the model type or its properties instead."); } [Fact] public void GetBinderThrowsIfBinderHasNoParameterlessConstructor() { // Arrange ControllerContext controllerContext = new ControllerContext(); ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType( null, typeof(ModelWithProviderAttribute_ProviderHasNoParameterlessConstructor)), }; // Act & Assert, confirming type name and full stack are available in Exception MissingMethodException exception = Assert.Throws( () => collection.GetBinder(controllerContext, bindingContext), "No parameterless constructor defined for this object. Object type 'Microsoft.Web.Mvc.ModelBinding.Test.ModelBinderProviderCollectionTest+NoParameterlessCtorProvider'."); Assert.Contains("System.Activator.CreateInstance(", exception.ToString()); } [Fact] public void GetBinderThrowsIfGenericProviderHasNoParameterlessConstructor() { // Arrange ControllerContext controllerContext = new ControllerContext(); ModelBinderProviderCollection collection = new ModelBinderProviderCollection(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType( null, typeof(ModelWithProviderAttribute_ProviderHasNoParameterlessConstructor)), }; // Act & Assert, confirming type name and full stack are available in Exception MissingMethodException exception = Assert.Throws( () => collection.GetBinder(controllerContext, bindingContext), "No parameterless constructor defined for this object. Object type 'Microsoft.Web.Mvc.ModelBinding.Test.ModelBinderProviderCollectionTest+NoParameterlessCtorBinder`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'."); Assert.Contains("System.Activator.CreateInstance(", exception.ToString()); } [Fact] public void GetRequiredBinderThrowsIfNoProviderMatches() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)) }; ModelBinderProviderCollection collection = new ModelBinderProviderCollection(new[] { new Mock().Object, }); // Act & assert Assert.Throws( delegate { collection.GetRequiredBinder(controllerContext, bindingContext); }, @"A binder for type System.Int32 could not be located."); } [MetadataType(typeof(ModelWithBindAttribute_Buddy))] private class ModelWithBindAttribute { [Bind] private class ModelWithBindAttribute_Buddy { } } [ModelBinderProviderOptions(FrontOfList = true)] private class ProviderAtFront : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } [ExtensibleModelBinder(typeof(object))] private class ModelWithProviderAttribute_BadAttribute { } [ExtensibleModelBinder(typeof(CustomBinder))] private class ModelWithProviderAttribute_Binder { } [ExtensibleModelBinder(typeof(CustomGenericBinder<>))] private class ModelWithProviderAttribute_Binder_Generic { } [ExtensibleModelBinder(typeof(CustomBinder), SuppressPrefixCheck = true)] private class ModelWithProviderAttribute_Binder_SuppressPrefix { } [ExtensibleModelBinder(typeof(CustomProvider))] private class ModelWithProviderAttribute_Provider { } [ExtensibleModelBinder(typeof(NoParameterlessCtorProvider))] private class ModelWithProviderAttribute_ProviderHasNoParameterlessConstructor { } [ExtensibleModelBinder(typeof(NoParameterlessCtorBinder<>))] private class ModelWithProviderAttribute_ProviderHasNoParameterlessConstructor { } private class CustomProvider : ModelBinderProvider { public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return new CustomBinder(); } } private class CustomBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } private class CustomGenericBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } private class NoParameterlessCtorProvider : ModelBinderProvider { public NoParameterlessCtorProvider(int parameter) { } public override IExtensibleModelBinder GetBinder(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } private class NoParameterlessCtorBinder : IExtensibleModelBinder { public NoParameterlessCtorBinder(int parameter) { } public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ModelBinderProvidersTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelBinderProvidersTest { [Fact] public void CollectionDefaults() { // Arrange Type[] expectedTypes = new[] { typeof(TypeMatchModelBinderProvider), typeof(BinaryDataModelBinderProvider), typeof(KeyValuePairModelBinderProvider), typeof(ComplexModelDtoModelBinderProvider), typeof(ArrayModelBinderProvider), typeof(DictionaryModelBinderProvider), typeof(CollectionModelBinderProvider), typeof(TypeConverterModelBinderProvider), typeof(MutableObjectModelBinderProvider) }; // Act Type[] actualTypes = ModelBinderProviders.Providers.Select(p => p.GetType()).ToArray(); // Assert Assert.Equal(expectedTypes, actualTypes); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ModelBinderUtilTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelBinderUtilTest { [Fact] public void CastOrDefault_CorrectType_ReturnsInput() { // Act int retVal = ModelBinderUtil.CastOrDefault(42); // Assert Assert.Equal(42, retVal); } [Fact] public void CastOrDefault_IncorrectType_ReturnsDefaultTModel() { // Act DateTime retVal = ModelBinderUtil.CastOrDefault(42); // Assert Assert.Equal(default(DateTime), retVal); } [Fact] public void CreateIndexModelName_EmptyParentName() { // Act string fullChildName = ModelBinderUtil.CreateIndexModelName("", 42); // Assert Assert.Equal("[42]", fullChildName); } [Fact] public void CreateIndexModelName_IntIndex() { // Act string fullChildName = ModelBinderUtil.CreateIndexModelName("parentName", 42); // Assert Assert.Equal("parentName[42]", fullChildName); } [Fact] public void CreateIndexModelName_StringIndex() { // Act string fullChildName = ModelBinderUtil.CreateIndexModelName("parentName", "index"); // Assert Assert.Equal("parentName[index]", fullChildName); } [Fact] public void CreatePropertyModelName() { // Act string fullChildName = ModelBinderUtil.CreatePropertyModelName("parentName", "childName"); // Assert Assert.Equal("parentName.childName", fullChildName); } [Fact] public void CreatePropertyModelName_EmptyParentName() { // Act string fullChildName = ModelBinderUtil.CreatePropertyModelName("", "childName"); // Assert Assert.Equal("childName", fullChildName); } [Fact] public void GetPossibleBinderInstance_Match_ReturnsBinder() { // Act IExtensibleModelBinder binder = ModelBinderUtil.GetPossibleBinderInstance(typeof(List), typeof(List<>), typeof(SampleGenericBinder<>)); // Assert Assert.IsType>(binder); } [Fact] public void GetPossibleBinderInstance_NoMatch_ReturnsNull() { // Act IExtensibleModelBinder binder = ModelBinderUtil.GetPossibleBinderInstance(typeof(ArraySegment), typeof(List<>), typeof(SampleGenericBinder<>)); // Assert Assert.Null(binder); } [Fact] public void RawValueToObjectArray_RawValueIsEnumerable_ReturnsInputAsArray() { // Assert List original = new List { 1, 2, 3, 4 }; // Act object[] retVal = ModelBinderUtil.RawValueToObjectArray(original); // Assert Assert.Equal(new object[] { 1, 2, 3, 4 }, retVal); } [Fact] public void RawValueToObjectArray_RawValueIsObject_WrapsObjectInSingleElementArray() { // Act object[] retVal = ModelBinderUtil.RawValueToObjectArray(42); // Assert Assert.Equal(new object[] { 42 }, retVal); } [Fact] public void RawValueToObjectArray_RawValueIsObjectArray_ReturnsInputInstance() { // Assert object[] original = new object[2]; // Act object[] retVal = ModelBinderUtil.RawValueToObjectArray(original); // Assert Assert.Same(original, retVal); } [Fact] public void RawValueToObjectArray_RawValueIsString_WrapsStringInSingleElementArray() { // Act object[] retVal = ModelBinderUtil.RawValueToObjectArray("hello"); // Assert Assert.Equal(new object[] { "hello" }, retVal); } [Fact] public void ReplaceEmptyStringWithNull_ConvertEmptyStringToNullDisabled_ModelIsEmptyString_LeavesModelAlone() { // Arrange ModelMetadata modelMetadata = GetMetadata(typeof(string)); modelMetadata.ConvertEmptyStringToNull = false; // Act object model = ""; ModelBinderUtil.ReplaceEmptyStringWithNull(modelMetadata, ref model); // Assert Assert.Equal("", model); } [Fact] public void ReplaceEmptyStringWithNull_ConvertEmptyStringToNullEnabled_ModelIsEmptyString_ReplacesModelWithNull() { // Arrange ModelMetadata modelMetadata = GetMetadata(typeof(string)); modelMetadata.ConvertEmptyStringToNull = true; // Act object model = ""; ModelBinderUtil.ReplaceEmptyStringWithNull(modelMetadata, ref model); // Assert Assert.Null(model); } [Fact] public void ReplaceEmptyStringWithNull_ConvertEmptyStringToNullEnabled_ModelIsWhitespaceString_ReplacesModelWithNull() { // Arrange ModelMetadata modelMetadata = GetMetadata(typeof(string)); modelMetadata.ConvertEmptyStringToNull = true; // Act object model = " "; // whitespace ModelBinderUtil.ReplaceEmptyStringWithNull(modelMetadata, ref model); // Assert Assert.Null(model); } [Fact] public void ReplaceEmptyStringWithNull_ConvertEmptyStringToNullDisabled_ModelIsNotEmptyString_LeavesModelAlone() { // Arrange ModelMetadata modelMetadata = GetMetadata(typeof(string)); modelMetadata.ConvertEmptyStringToNull = true; // Act object model = 42; ModelBinderUtil.ReplaceEmptyStringWithNull(modelMetadata, ref model); // Assert Assert.Equal(42, model); } [Fact] public void ValidateBindingContext_SuccessWithNonNullModel() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadata(typeof(string)) }; bindingContext.ModelMetadata.Model = "hello!"; // Act ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), false); // Assert // Nothing to do - if we got this far without throwing, the test succeeded } [Fact] public void ValidateBindingContext_SuccessWithNullModel() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadata(typeof(string)) }; // Act ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), true); // Assert // Nothing to do - if we got this far without throwing, the test succeeded } [Fact] public void ValidateBindingContextThrowsIfBindingContextIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { ModelBinderUtil.ValidateBindingContext(null, typeof(string), true); }, "bindingContext"); } [Fact] public void ValidateBindingContextThrowsIfModelInstanceIsWrongType() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadata(typeof(string)) }; bindingContext.ModelMetadata.Model = 42; // Act & assert Assert.Throws( delegate { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), true); }, "The binding context has a Model of type 'System.Int32', but this binder can only operate on models of type 'System.String'." + Environment.NewLine + "Parameter name: bindingContext"); } [Fact] public void ValidateBindingContextThrowsIfModelIsNullButCannotBe() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadata(typeof(string)) }; // Act & assert Assert.Throws( delegate { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), false); }, "The binding context has a null Model, but this binder requires a non-null model of type 'System.String'." + Environment.NewLine + "Parameter name: bindingContext"); } [Fact] public void ValidateBindingContextThrowsIfModelMetadataIsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext(); // Act & assert Assert.Throws( delegate { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), true); }, "The binding context cannot have a null ModelMetadata." + Environment.NewLine + "Parameter name: bindingContext"); } [Fact] public void ValidateBindingContextThrowsIfModelTypeIsWrong() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadata(typeof(object)) }; // Act & assert Assert.Throws( delegate { ModelBinderUtil.ValidateBindingContext(bindingContext, typeof(string), true); }, "The binding context has a ModelType of 'System.Object', but this binder can only operate on models of type 'System.String'." + Environment.NewLine + "Parameter name: bindingContext"); } private static ModelMetadata GetMetadata(Type modelType) { EmptyModelMetadataProvider provider = new EmptyModelMetadataProvider(); return provider.GetMetadataForType(null, modelType); } private class SampleGenericBinder : IExtensibleModelBinder { public bool BindModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { throw new NotImplementedException(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/ModelValidationNodeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class ModelValidationNodeTest { [Fact] public void ConstructorSetsCollectionInstance() { // Arrange ModelMetadata metadata = GetModelMetadata(); string modelStateKey = "someKey"; ModelValidationNode[] childNodes = new[] { new ModelValidationNode(metadata, "someKey0"), new ModelValidationNode(metadata, "someKey1") }; // Act ModelValidationNode node = new ModelValidationNode(metadata, modelStateKey, childNodes); // Assert Assert.Equal(childNodes, node.ChildNodes.ToArray()); } [Fact] public void ConstructorThrowsIfModelMetadataIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new ModelValidationNode(null, "someKey"); }, "modelMetadata"); } [Fact] public void ConstructorThrowsIfModelStateKeyIsNull() { // Arrange ModelMetadata metadata = GetModelMetadata(); // Act & assert Assert.ThrowsArgumentNull( delegate { new ModelValidationNode(metadata, null); }, "modelStateKey"); } [Fact] public void PropertiesAreSet() { // Arrange ModelMetadata metadata = GetModelMetadata(); string modelStateKey = "someKey"; // Act ModelValidationNode node = new ModelValidationNode(metadata, modelStateKey); // Assert Assert.Equal(metadata, node.ModelMetadata); Assert.Equal(modelStateKey, node.ModelStateKey); Assert.NotNull(node.ChildNodes); Assert.Empty(node.ChildNodes); } [Fact] public void CombineWith() { // Arrange List log = new List(); ModelValidationNode[] allChildNodes = new[] { new ModelValidationNode(GetModelMetadata(), "key1"), new ModelValidationNode(GetModelMetadata(), "key2"), new ModelValidationNode(GetModelMetadata(), "key3"), }; ModelValidationNode parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1"); parentNode1.ChildNodes.Add(allChildNodes[0]); parentNode1.Validating += delegate { log.Add("Validating parent1."); }; parentNode1.Validated += delegate { log.Add("Validated parent1."); }; ModelValidationNode parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2"); parentNode2.ChildNodes.Add(allChildNodes[1]); parentNode2.ChildNodes.Add(allChildNodes[2]); parentNode2.Validating += delegate { log.Add("Validating parent2."); }; parentNode2.Validated += delegate { log.Add("Validated parent2."); }; // Act parentNode1.CombineWith(parentNode2); parentNode1.Validate(new ControllerContext { Controller = new EmptyController() }); // Assert Assert.Equal(new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }, log.ToArray()); Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray()); } [Fact] public void CombineWith_OtherNodeIsSuppressed_DoesNothing() { // Arrange List log = new List(); ModelValidationNode[] allChildNodes = new[] { new ModelValidationNode(GetModelMetadata(), "key1"), new ModelValidationNode(GetModelMetadata(), "key2"), new ModelValidationNode(GetModelMetadata(), "key3"), }; ModelValidationNode[] expectedChildNodes = new[] { allChildNodes[0] }; ModelValidationNode parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1"); parentNode1.ChildNodes.Add(allChildNodes[0]); parentNode1.Validating += delegate { log.Add("Validating parent1."); }; parentNode1.Validated += delegate { log.Add("Validated parent1."); }; ModelValidationNode parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2"); parentNode2.ChildNodes.Add(allChildNodes[1]); parentNode2.ChildNodes.Add(allChildNodes[2]); parentNode2.Validating += delegate { log.Add("Validating parent2."); }; parentNode2.Validated += delegate { log.Add("Validated parent2."); }; parentNode2.SuppressValidation = true; // Act parentNode1.CombineWith(parentNode2); parentNode1.Validate(new ControllerContext { Controller = new EmptyController() }); // Assert Assert.Equal(new[] { "Validating parent1.", "Validated parent1." }, log.ToArray()); Assert.Equal(expectedChildNodes, parentNode1.ChildNodes.ToArray()); } [Fact] public void Validate_Ordering() { // Proper order of invocation: // 1. OnValidating() // 2. Child validators // 3. This validator // 4. OnValidated() // Arrange List log = new List(); LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log); ModelMetadata modelMetadata = GetModelMetadata(model); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey"); ModelMetadata childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "ValidStringProperty"); node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.ValidStringProperty")); node.Validating += delegate { log.Add("In OnValidating()"); }; node.Validated += delegate { log.Add("In OnValidated()"); }; // Act node.Validate(controllerContext); // Assert Assert.Equal(new[] { "In OnValidating()", "In IDataErrorInfo.get_Item('ValidStringProperty')", "In IDataErrorInfo.get_Error()", "In OnValidated()" }, log.ToArray()); } [Fact] public void Validate_PassesNullContainerInstanceIfCannotBeConvertedToProperType() { // Arrange List log1 = new List(); LoggingDataErrorInfoModel model1 = new LoggingDataErrorInfoModel(log1); ModelMetadata modelMetadata1 = GetModelMetadata(model1); List log2 = new List(); LoggingDataErrorInfoModel model2 = new LoggingDataErrorInfoModel(log2); ModelMetadata modelMetadata2 = GetModelMetadata(model2); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata1, "theKey"); node.ChildNodes.Add(new ModelValidationNode(modelMetadata2, "theKey.SomeProperty")); // Act node.Validate(controllerContext); // Assert Assert.Equal(new[] { "In IDataErrorInfo.get_Error()" }, log1.ToArray()); Assert.Equal(new[] { "In IDataErrorInfo.get_Error()" }, log2.ToArray()); } [Fact] public void Validate_SkipsRemainingValidationIfModelStateIsInvalid() { // Because a property validator fails, the model validator shouldn't run // Arrange List log = new List(); LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log); ModelMetadata modelMetadata = GetModelMetadata(model); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey"); ModelMetadata childMetadata = new EmptyModelMetadataProvider().GetMetadataForProperty(() => model, model.GetType(), "InvalidStringProperty"); node.ChildNodes.Add(new ModelValidationNode(childMetadata, "theKey.InvalidStringProperty")); node.Validating += delegate { log.Add("In OnValidating()"); }; node.Validated += delegate { log.Add("In OnValidated()"); }; // Act node.Validate(controllerContext); // Assert Assert.Equal(new[] { "In OnValidating()", "In IDataErrorInfo.get_Item('InvalidStringProperty')", "In OnValidated()" }, log.ToArray()); Assert.Equal("Sample error message", controllerContext.Controller.ViewData.ModelState["theKey.InvalidStringProperty"].Errors[0].ErrorMessage); } [Fact] public void Validate_SkipsValidationIfHandlerCancels() { // Arrange List log = new List(); LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log); ModelMetadata modelMetadata = GetModelMetadata(model); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey"); node.Validating += (sender, e) => { log.Add("In OnValidating()"); e.Cancel = true; }; node.Validated += delegate { log.Add("In OnValidated()"); }; // Act node.Validate(controllerContext); // Assert Assert.Equal(new[] { "In OnValidating()" }, log.ToArray()); } [Fact] public void Validate_SkipsValidationIfSuppressed() { // Arrange List log = new List(); LoggingDataErrorInfoModel model = new LoggingDataErrorInfoModel(log); ModelMetadata modelMetadata = GetModelMetadata(model); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey") { SuppressValidation = true }; node.Validating += (sender, e) => { log.Add("In OnValidating()"); }; node.Validated += delegate { log.Add("In OnValidated()"); }; // Act node.Validate(controllerContext); // Assert Assert.Empty(log); } [Fact] public void Validate_ThrowsIfControllerContextIsNull() { // Arrange ModelValidationNode node = new ModelValidationNode(GetModelMetadata(), "someKey"); // Act & assert Assert.ThrowsArgumentNull( delegate { node.Validate(null); }, "controllerContext"); } [Fact] [ReplaceCulture] public void Validate_ValidateAllProperties_AddsValidationErrors() { // Arrange ValidateAllPropertiesModel model = new ValidateAllPropertiesModel { RequiredString = null /* error */, RangedInt = 0 /* error */, ValidString = "dog" }; ModelMetadata modelMetadata = GetModelMetadata(model); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelValidationNode node = new ModelValidationNode(modelMetadata, "theKey") { ValidateAllProperties = true }; controllerContext.Controller.ViewData.ModelState.AddModelError("theKey.RequiredString.Dummy", "existing Error Text"); // Act node.Validate(controllerContext); // Assert Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey.RequiredString"]); Assert.Equal("existing Error Text", controllerContext.Controller.ViewData.ModelState["theKey.RequiredString.Dummy"].Errors[0].ErrorMessage); Assert.Equal("The field RangedInt must be between 10 and 30.", controllerContext.Controller.ViewData.ModelState["theKey.RangedInt"].Errors[0].ErrorMessage); Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey.ValidString"]); Assert.Null(controllerContext.Controller.ViewData.ModelState["theKey"]); } private static ModelMetadata GetModelMetadata() { EmptyModelMetadataProvider provider = new EmptyModelMetadataProvider(); return provider.GetMetadataForType(null, typeof(object)); } private static ModelMetadata GetModelMetadata(object o) { DataAnnotationsModelMetadataProvider provider = new DataAnnotationsModelMetadataProvider(); return provider.GetMetadataForType(() => o, o.GetType()); } private sealed class EmptyController : Controller { } private sealed class LoggingDataErrorInfoModel : IDataErrorInfo { private readonly IList _log; public LoggingDataErrorInfoModel(IList log) { _log = log; } string IDataErrorInfo.Error { get { _log.Add("In IDataErrorInfo.get_Error()"); return null; } } string IDataErrorInfo.this[string columnName] { get { _log.Add("In IDataErrorInfo.get_Item('" + columnName + "')"); return (columnName == "ValidStringProperty") ? null : "Sample error message"; } } public string ValidStringProperty { get; set; } public string InvalidStringProperty { get; set; } } private class ValidateAllPropertiesModel { [Required] public string RequiredString { get; set; } [Range(10, 30)] public int RangedInt { get; set; } [RegularExpression("dog")] public string ValidString { get; set; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/MutableObjectModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class MutableObjectModelBinderProviderTest { [Fact] public void GetBinder_NoPrefixInValueProvider_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int)), ModelName = "foo", ValueProvider = new SimpleValueProvider() }; MutableObjectModelBinderProvider binderProvider = new MutableObjectModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_PrefixInValueProvider_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => 42, typeof(int)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.bar", "someValue" } } }; MutableObjectModelBinderProvider binderProvider = new MutableObjectModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.NotNull(binder); Assert.IsType(binder); } [Fact] public void GetBinder_TypeIsComplexModelDto_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(ComplexModelDto)), ModelName = "foo", ValueProvider = new SimpleValueProvider { { "foo.bar", "someValue" } } }; MutableObjectModelBinderProvider binderProvider = new MutableObjectModelBinderProvider(); // Act IExtensibleModelBinder binder = binderProvider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/MutableObjectModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class MutableObjectModelBinderTest { [Fact] public void BindModel() { // Arrange ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelBinderProviders = new ModelBinderProviderCollection(), ModelMetadata = GetMetadataForObject(new Person()), ModelName = "someName" }; Mock mockDtoBinder = new Mock(); mockDtoBinder .Setup(o => o.BindModel(controllerContext, It.IsAny())) .Returns( delegate(ControllerContext cc, ExtensibleModelBindingContext mbc2) { return true; // just return the DTO unchanged }); bindingContext.ModelBinderProviders.RegisterBinderForType(typeof(ComplexModelDto), mockDtoBinder.Object, true /* suppressPrefixCheck */); Mock mockTestableBinder = new Mock { CallBase = true }; mockTestableBinder.Setup(o => o.EnsureModelPublic(controllerContext, bindingContext)).Verifiable(); mockTestableBinder.Setup(o => o.GetMetadataForPropertiesPublic(controllerContext, bindingContext)).Returns(new ModelMetadata[0]).Verifiable(); TestableMutableObjectModelBinder testableBinder = mockTestableBinder.Object; testableBinder.MetadataProvider = new DataAnnotationsModelMetadataProvider(); // Act bool retValue = testableBinder.BindModel(controllerContext, bindingContext); // Assert Assert.True(retValue); Assert.IsType(bindingContext.Model); Assert.True(bindingContext.ValidationNode.ValidateAllProperties); mockTestableBinder.Verify(); } [Fact] public void CanUpdateProperty_HasPublicSetter_ReturnsTrue() { // Arrange ModelMetadata propertyMetadata = GetMetadataForCanUpdateProperty("ReadWriteString"); // Act bool canUpdate = MutableObjectModelBinder.CanUpdatePropertyInternal(propertyMetadata); // Assert Assert.True(canUpdate); } [Fact] public void CanUpdateProperty_ReadOnlyArray_ReturnsFalse() { // Arrange ModelMetadata propertyMetadata = GetMetadataForCanUpdateProperty("ReadOnlyArray"); // Act bool canUpdate = MutableObjectModelBinder.CanUpdatePropertyInternal(propertyMetadata); // Assert Assert.False(canUpdate); } [Fact] public void CanUpdateProperty_ReadOnlyReferenceTypeNotBlacklisted_ReturnsTrue() { // Arrange ModelMetadata propertyMetadata = GetMetadataForCanUpdateProperty("ReadOnlyObject"); // Act bool canUpdate = MutableObjectModelBinder.CanUpdatePropertyInternal(propertyMetadata); // Assert Assert.True(canUpdate); } [Fact] public void CanUpdateProperty_ReadOnlyString_ReturnsFalse() { // Arrange ModelMetadata propertyMetadata = GetMetadataForCanUpdateProperty("ReadOnlyString"); // Act bool canUpdate = MutableObjectModelBinder.CanUpdatePropertyInternal(propertyMetadata); // Assert Assert.False(canUpdate); } [Fact] public void CanUpdateProperty_ReadOnlyValueType_ReturnsFalse() { // Arrange ModelMetadata propertyMetadata = GetMetadataForCanUpdateProperty("ReadOnlyInt"); // Act bool canUpdate = MutableObjectModelBinder.CanUpdatePropertyInternal(propertyMetadata); // Assert Assert.False(canUpdate); } [Fact] public void CreateModel() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(Person)) }; TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act object retModel = testableBinder.CreateModelPublic(null, bindingContext); // Assert Assert.IsType(retModel); } [Fact] public void CreateModelThrowsIfModelTypeHasNoParameterlessConstructor() { // Arrange TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(NoParameterlessCtor)), }; // Act & Assert, confirming type name and full stack are available in Exception MissingMethodException exception = Assert.Throws( () => testableBinder.CreateModelPublic(null, bindingContext), "No parameterless constructor defined for this object. Object type 'Microsoft.Web.Mvc.ModelBinding.Test.MutableObjectModelBinderTest+NoParameterlessCtor'."); Assert.Contains("System.Activator.CreateInstance(", exception.ToString()); } [Fact] public void EnsureModel_ModelIsNotNull_DoesNothing() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new Person()) }; Mock mockTestableBinder = new Mock { CallBase = true }; TestableMutableObjectModelBinder testableBinder = mockTestableBinder.Object; // Act object originalModel = bindingContext.Model; testableBinder.EnsureModelPublic(null, bindingContext); object newModel = bindingContext.Model; // Assert Assert.Same(originalModel, newModel); mockTestableBinder.Verify(o => o.CreateModelPublic(null, bindingContext), Times.Never()); } [Fact] public void EnsureModel_ModelIsNull_CallsCreateModel() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(Person)) }; Mock mockTestableBinder = new Mock { CallBase = true }; mockTestableBinder.Setup(o => o.CreateModelPublic(null, bindingContext)).Returns(new Person()).Verifiable(); TestableMutableObjectModelBinder testableBinder = mockTestableBinder.Object; // Act object originalModel = bindingContext.Model; testableBinder.EnsureModelPublic(null, bindingContext); object newModel = bindingContext.Model; // Assert Assert.Null(originalModel); Assert.IsType(newModel); mockTestableBinder.Verify(); } [Fact] public void GetMetadataForProperties_WithBindAttribute() { // Arrange string[] expectedPropertyNames = new[] { "FirstName", "LastName" }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion)) }; TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act IEnumerable propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(null, bindingContext); string[] returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray(); // Assert Assert.Equal(expectedPropertyNames, returnedPropertyNames); } [Fact] public void GetMetadataForProperties_WithoutBindAttribute() { // Arrange string[] expectedPropertyNames = new[] { "DateOfBirth", "DateOfDeath", "ValueTypeRequired", "FirstName", "LastName", "PropertyWithDefaultValue" }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(Person)) }; TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act IEnumerable propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(null, bindingContext); string[] returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray(); // Assert Assert.Equal(expectedPropertyNames, returnedPropertyNames); } [Fact] public void GetRequiredPropertiesCollection_MixedAttributes() { // Arrange Type modelType = typeof(ModelWithMixedBindingBehaviors); // Act HashSet requiredProperties; HashSet skipProperties; MutableObjectModelBinder.GetRequiredPropertiesCollection(modelType, out requiredProperties, out skipProperties); // Assert Assert.Equal(new[] { "Required" }, requiredProperties.ToArray()); Assert.Equal(new[] { "Never" }, skipProperties.ToArray()); } [Fact] public void NullCheckFailedHandler_ModelStateAlreadyInvalid_DoesNothing() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; controllerContext.Controller.ViewData.ModelState.AddModelError("foo.bar", "Some existing error."); ModelMetadata modelMetadata = GetMetadataForType(typeof(Person)); ModelValidationNode validationNode = new ModelValidationNode(modelMetadata, "foo"); ModelValidatedEventArgs e = new ModelValidatedEventArgs(controllerContext, null /* parentNode */); // Act EventHandler handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(controllerContext, modelMetadata, null /* incomingValue */); handler(validationNode, e); // Assert Assert.False(controllerContext.Controller.ViewData.ModelState.ContainsKey("foo")); } [Fact] public void NullCheckFailedHandler_ModelStateValid_AddsErrorString() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelMetadata modelMetadata = GetMetadataForType(typeof(Person)); ModelValidationNode validationNode = new ModelValidationNode(modelMetadata, "foo"); ModelValidatedEventArgs e = new ModelValidatedEventArgs(controllerContext, null /* parentNode */); // Act EventHandler handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(controllerContext, modelMetadata, null /* incomingValue */); handler(validationNode, e); // Assert Assert.True(controllerContext.Controller.ViewData.ModelState.ContainsKey("foo")); Assert.Equal("A value is required.", controllerContext.Controller.ViewData.ModelState["foo"].Errors[0].ErrorMessage); } [Fact] public void NullCheckFailedHandler_ModelStateValid_CallbackReturnsNull_DoesNothing() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ModelMetadata modelMetadata = GetMetadataForType(typeof(Person)); ModelValidationNode validationNode = new ModelValidationNode(modelMetadata, "foo"); ModelValidatedEventArgs e = new ModelValidatedEventArgs(controllerContext, null /* parentNode */); // Act ModelBinderErrorMessageProvider originalProvider = ModelBinderConfig.ValueRequiredErrorMessageProvider; try { ModelBinderConfig.ValueRequiredErrorMessageProvider = delegate { return null; }; EventHandler handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(controllerContext, modelMetadata, null /* incomingValue */); handler(validationNode, e); } finally { ModelBinderConfig.ValueRequiredErrorMessageProvider = originalProvider; } // Assert Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid); } [Fact] public void ProcessDto_BindRequiredFieldMissing_Throws() { // Arrange ModelWithBindRequired model = new ModelWithBindRequired { Name = "original value", Age = -20 }; ModelMetadata containerMetadata = GetMetadataForObject(model); ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = containerMetadata, ModelName = "theModel" }; ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties); ModelMetadata nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name"); dto.Results[nameProperty] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(nameProperty, "")); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act & assert Assert.Throws( delegate { testableBinder.ProcessDto(controllerContext, bindingContext, dto); }, @"A value for 'theModel.Age' is required but was not present in the request."); Assert.Equal("original value", model.Name); Assert.Equal(-20, model.Age); } [Fact] public void ProcessDto_Success() { // Arrange DateTime dob = new DateTime(2001, 1, 1); Person model = new Person { DateOfBirth = dob }; ModelMetadata containerMetadata = GetMetadataForObject(model); ControllerContext controllerContext = new ControllerContext(); ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = containerMetadata }; ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties); ModelMetadata firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName"); dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, "")); ModelMetadata lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName"); dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, "")); ModelMetadata dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth"); dto.Results[dobProperty] = null; TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.ProcessDto(controllerContext, bindingContext, dto); // Assert Assert.Equal("John", model.FirstName); Assert.Equal("Doe", model.LastName); Assert.Equal(dob, model.DateOfBirth); Assert.True(bindingContext.ModelState.IsValid); } [Fact] public void SetProperty_PropertyHasDefaultValue_SetsDefaultValue() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new Person()) }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "PropertyWithDefaultValue"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert var person = Assert.IsType(bindingContext.Model); Assert.Equal(123.456m, person.PropertyWithDefaultValue); Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid); } [Fact] public void SetProperty_PropertyIsReadOnly_DoesNothing() { // Arrange ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForType(typeof(Person)) }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(null, bindingContext, propertyMetadata, dtoResult); // Assert // If didn't throw, success! } [Fact] public void SetProperty_PropertyIsSettable_CallsSetter() { // Arrange Person model = new Person(); ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(model) }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert validationNode.Validate(controllerContext); Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid); Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth); } [Fact] [ReplaceCulture] public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError() { // Arrange Person model = new Person { DateOfBirth = new DateTime(1900, 1, 1) }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(model) }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(new DateTime(1800, 1, 1), validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(null, bindingContext, propertyMetadata, dtoResult); // Assert Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine + "Parameter name: value", bindingContext.ModelState["foo"].Errors[0].Exception.Message); } [Fact] public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new Person()), }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert Assert.True(controllerContext.Controller.ViewData.ModelState.IsValid); validationNode.Validate(controllerContext, bindingContext.ValidationNode); Assert.False(controllerContext.Controller.ViewData.ModelState.IsValid); } [Fact] public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new Person()), ModelName = "foo" }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert Assert.False(bindingContext.ModelState.IsValid); Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage); } [Fact] [ReplaceCulture] public void SetProperty_SettingNullableTypeToNull_RequiredValidatorNotPresent_PropertySetterThrows_AddsRequiredMessageString() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new ModelWhosePropertySetterThrows()), ModelName = "foo" }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NameNoAttribute"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo.NameNoAttribute"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert Assert.False(bindingContext.ModelState.IsValid); ModelError error = Assert.Single(bindingContext.ModelState["foo.NameNoAttribute"].Errors); Assert.Equal("This is a different exception." + Environment.NewLine + "Parameter name: value", error.Exception.Message); } [Fact] public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString() { // Arrange ControllerContext controllerContext = new ControllerContext { Controller = new EmptyController() }; ExtensibleModelBindingContext bindingContext = new ExtensibleModelBindingContext { ModelMetadata = GetMetadataForObject(new ModelWhosePropertySetterThrows()), ModelName = "foo" }; ModelMetadata propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name"); ModelValidationNode validationNode = new ModelValidationNode(propertyMetadata, "foo.Name"); ComplexModelDtoResult dtoResult = new ComplexModelDtoResult(null /* model */, validationNode); TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); // Assert Assert.False(bindingContext.ModelState.IsValid); ModelError error = Assert.Single(bindingContext.ModelState["foo.Name"].Errors); Assert.Equal("This message comes from the [Required] attribute.", error.ErrorMessage); } private static ModelMetadata GetMetadataForCanUpdateProperty(string propertyName) { DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForProperty(null, typeof(MyModelTestingCanUpdateProperty), propertyName); } private static ModelMetadata GetMetadataForObject(object o) { DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForType(() => o, o.GetType()); } private static ModelMetadata GetMetadataForType(Type t) { DataAnnotationsModelMetadataProvider metadataProvider = new DataAnnotationsModelMetadataProvider(); return metadataProvider.GetMetadataForType(null, t); } private class Person { private DateTime? _dateOfDeath; public DateTime DateOfBirth { get; set; } public DateTime? DateOfDeath { get { return _dateOfDeath; } set { if (value < DateOfBirth) { throw new ArgumentOutOfRangeException("value", "Date of death can't be before date of birth."); } _dateOfDeath = value; } } [Required(ErrorMessage = "Sample message")] public int ValueTypeRequired { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string NonUpdateableProperty { get; private set; } [DefaultValue(typeof(decimal), "123.456")] public decimal PropertyWithDefaultValue { get; set; } } private class NoParameterlessCtor { public NoParameterlessCtor(int parameter) { } } private class PersonWithBindExclusion { [BindNever] public DateTime DateOfBirth { get; set; } [BindNever] public DateTime? DateOfDeath { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string NonUpdateableProperty { get; private set; } } private class ModelWithBindRequired { public string Name { get; set; } [BindRequired] public int Age { get; set; } } [BindRequired] private class ModelWithMixedBindingBehaviors { public string Required { get; set; } [BindNever] public string Never { get; set; } [BindingBehavior(BindingBehavior.Optional)] public string Optional { get; set; } } private sealed class MyModelTestingCanUpdateProperty { public int ReadOnlyInt { get; private set; } public string ReadOnlyString { get; private set; } public string[] ReadOnlyArray { get; private set; } public object ReadOnlyObject { get; private set; } public string ReadWriteString { get; set; } } private sealed class ModelWhosePropertySetterThrows { [Required(ErrorMessage = "This message comes from the [Required] attribute.")] public string Name { get { return null; } set { throw new ArgumentException("This is an exception.", "value"); } } public string NameNoAttribute { get { return null; } set { throw new ArgumentException("This is a different exception.", "value"); } } } public class TestableMutableObjectModelBinder : MutableObjectModelBinder { public virtual bool CanUpdatePropertyPublic(ModelMetadata propertyMetadata) { return base.CanUpdateProperty(propertyMetadata); } protected override bool CanUpdateProperty(ModelMetadata propertyMetadata) { return CanUpdatePropertyPublic(propertyMetadata); } public virtual object CreateModelPublic(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return base.CreateModel(controllerContext, bindingContext); } protected override object CreateModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return CreateModelPublic(controllerContext, bindingContext); } public virtual void EnsureModelPublic(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { base.EnsureModel(controllerContext, bindingContext); } protected override void EnsureModel(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { EnsureModelPublic(controllerContext, bindingContext); } public virtual IEnumerable GetMetadataForPropertiesPublic(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return base.GetMetadataForProperties(controllerContext, bindingContext); } protected override IEnumerable GetMetadataForProperties(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext) { return GetMetadataForPropertiesPublic(controllerContext, bindingContext); } public virtual void SetPropertyPublic(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, ModelMetadata propertyMetadata, ComplexModelDtoResult dtoResult) { base.SetProperty(controllerContext, bindingContext, propertyMetadata, dtoResult); } protected override void SetProperty(ControllerContext controllerContext, ExtensibleModelBindingContext bindingContext, ModelMetadata propertyMetadata, ComplexModelDtoResult dtoResult) { SetPropertyPublic(controllerContext, bindingContext, propertyMetadata, dtoResult); } } private class EmptyController : Controller { } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/SimpleModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class SimpleModelBinderProviderTest { [Fact] public void ConstructorWithFactoryThrowsIfModelBinderFactoryIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new SimpleModelBinderProvider(typeof(object), (Func)null); }, "modelBinderFactory"); } [Fact] public void ConstructorWithFactoryThrowsIfModelTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new SimpleModelBinderProvider(null, () => null); }, "modelType"); } [Fact] public void ConstructorWithInstanceThrowsIfModelBinderIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new SimpleModelBinderProvider(typeof(object), (IExtensibleModelBinder)null); }, "modelBinder"); } [Fact] public void ConstructorWithInstanceThrowsIfModelTypeIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new SimpleModelBinderProvider(null, new Mock().Object); }, "modelType"); } [Fact] public void GetBinder_TypeDoesNotMatch_ReturnsNull() { // Arrange SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), new Mock().Object) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(object)); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_TypeMatches_PrefixNotFound_ReturnsNull() { // Arrange IExtensibleModelBinder binderInstance = new Mock().Object; SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), binderInstance); ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(string)); bindingContext.ValueProvider = new SimpleValueProvider(); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(returnedBinder); } [Fact] public void GetBinder_TypeMatches_PrefixSuppressed_ReturnsFactoryInstance() { // Arrange int numExecutions = 0; IExtensibleModelBinder theBinderInstance = new Mock().Object; Func factory = delegate { numExecutions++; return theBinderInstance; }; SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), factory) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(string)); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Equal(2, numExecutions); Assert.Equal(theBinderInstance, returnedBinder); } [Fact] public void GetBinder_TypeMatches_PrefixSuppressed_ReturnsInstance() { // Arrange IExtensibleModelBinder theBinderInstance = new Mock().Object; SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), theBinderInstance) { SuppressPrefixCheck = true }; ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(string)); // Act IExtensibleModelBinder returnedBinder = provider.GetBinder(null, bindingContext); // Assert Assert.Equal(theBinderInstance, returnedBinder); } [Fact] public void GetBinderThrowsIfBindingContextIsNull() { // Arrange SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), new Mock().Object); // Act & assert Assert.ThrowsArgumentNull( delegate { provider.GetBinder(null, null); }, "bindingContext"); } [Fact] public void ModelTypeProperty() { // Arrange SimpleModelBinderProvider provider = new SimpleModelBinderProvider(typeof(string), new Mock().Object); // Act & assert Assert.Equal(typeof(string), provider.ModelType); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(() => null, modelType) }; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/TypeConverterModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class TypeConverterModelBinderProviderTest { [Fact] public void GetBinder_NoTypeConverterExistsFromString_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(void)); // no TypeConverter exists Void -> String TypeConverterModelBinderProvider provider = new TypeConverterModelBinderProvider(); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_NullValueProviderResult_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); bindingContext.ValueProvider = new SimpleValueProvider(); // clear the ValueProvider TypeConverterModelBinderProvider provider = new TypeConverterModelBinderProvider(); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void GetBinder_TypeConverterExistsFromString_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); // TypeConverter exists Int32 -> String TypeConverterModelBinderProvider provider = new TypeConverterModelBinderProvider(); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.IsType(binder); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType), ModelName = "theModelName", ValueProvider = new SimpleValueProvider { { "theModelName", "someValue" } } }; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/TypeConverterModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.ComponentModel; using System.Globalization; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class TypeConverterModelBinderTest { [Fact] public void BindModel_Error_FormatExceptionsTurnedIntoStringsInModelState() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); Assert.Equal("The value 'not an integer' is not valid for Int32.", bindingContext.ModelState["theModelName"].Errors[0].ErrorMessage); } [Fact] public void BindModel_Error_FormatExceptionsTurnedIntoStringsInModelState_ErrorNotAddedIfCallbackReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act ModelBinderErrorMessageProvider originalProvider = ModelBinderConfig.TypeConversionErrorMessageProvider; bool retVal; try { ModelBinderConfig.TypeConversionErrorMessageProvider = delegate { return null; }; retVal = binder.BindModel(null, bindingContext); } finally { ModelBinderConfig.TypeConversionErrorMessageProvider = originalProvider; } // Assert Assert.False(retVal); Assert.Null(bindingContext.Model); Assert.True(bindingContext.ModelState.IsValid); } [Fact] public void BindModel_Error_GeneralExceptionsSavedInModelState() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(Dummy)); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "foo" } }; TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); Assert.Null(bindingContext.Model); Assert.Equal("The parameter conversion from type 'System.String' to type 'Microsoft.Web.Mvc.ModelBinding.Test.TypeConverterModelBinderTest+Dummy' failed. See the inner exception for more information.", bindingContext.ModelState["theModelName"].Errors[0].Exception.Message); Assert.Equal("From DummyTypeConverter: foo", bindingContext.ModelState["theModelName"].Errors[0].Exception.InnerException.Message); } [Fact] public void BindModel_NullValueProviderResult_ReturnsFalse() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal, "BindModel should have returned null."); Assert.Empty(bindingContext.ModelState); } [Fact] public void BindModel_ValidValueProviderResult_ConvertEmptyStringsToNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(string)); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "" } }; TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.True(retVal); Assert.Null(bindingContext.Model); Assert.True(bindingContext.ModelState.ContainsKey("theModelName")); } [Fact] public void BindModel_ValidValueProviderResult_ReturnsModel() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(typeof(int)); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "42" } }; TypeConverterModelBinder binder = new TypeConverterModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.True(retVal); Assert.Equal(42, bindingContext.Model); Assert.True(bindingContext.ModelState.ContainsKey("theModelName")); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType), ModelName = "theModelName", ValueProvider = new SimpleValueProvider() // empty }; } [TypeConverter(typeof(DummyTypeConverter))] private struct Dummy { } private sealed class DummyTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return (sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { throw new InvalidOperationException(String.Format("From DummyTypeConverter: {0}", value)); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/TypeMatchModelBinderProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class TypeMatchModelBinderProviderTest { [Fact] public void ProviderIsMarkedFrontOfList() { // Arrange Type t = typeof(TypeMatchModelBinderProvider); // Act & assert Assert.True(t.GetCustomAttributes(typeof(ModelBinderProviderOptionsAttribute), true /* inherit */).Cast().Single().FrontOfList); } [Fact] public void GetBinder_InvalidValueProviderResult_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; TypeMatchModelBinderProvider provider = new TypeMatchModelBinderProvider(); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.Null(binder); } [Fact] public void BindModel_ValidValueProviderResult_ReturnsBinder() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", 42 } }; TypeMatchModelBinderProvider provider = new TypeMatchModelBinderProvider(); // Act IExtensibleModelBinder binder = provider.GetBinder(null, bindingContext); // Assert Assert.IsType(binder); } private static ExtensibleModelBindingContext GetBindingContext() { return GetBindingContext(typeof(int)); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType), ModelName = "theModelName" }; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/ModelBinding/Test/TypeMatchModelBinderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.ModelBinding.Test { public class TypeMatchModelBinderTest { [Fact] public void BindModel_InvalidValueProviderResult_ReturnsFalse() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; TypeMatchModelBinder binder = new TypeMatchModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.False(retVal); Assert.Empty(bindingContext.ModelState); } [Fact] public void BindModel_ValidValueProviderResult_ReturnsTrue() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", 42 } }; TypeMatchModelBinder binder = new TypeMatchModelBinder(); // Act bool retVal = binder.BindModel(null, bindingContext); // Assert Assert.True(retVal); Assert.Equal(42, bindingContext.Model); Assert.True(bindingContext.ModelState.ContainsKey("theModelName")); } [Fact] public void GetCompatibleValueProviderResult_ValueProviderResultRawValueIncorrect_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", "not an integer" } }; // Act ValueProviderResult vpResult = TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); // Assert Assert.Null(vpResult); // Raw value is the wrong type } [Fact] public void GetCompatibleValueProviderResult_ValueProviderResultValid_ReturnsValueProviderResult() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider { { "theModelName", 42 } }; // Act ValueProviderResult vpResult = TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); // Assert Assert.NotNull(vpResult); } [Fact] public void GetCompatibleValueProviderResult_ValueProviderReturnsNull_ReturnsNull() { // Arrange ExtensibleModelBindingContext bindingContext = GetBindingContext(); bindingContext.ValueProvider = new SimpleValueProvider(); // Act ValueProviderResult vpResult = TypeMatchModelBinder.GetCompatibleValueProviderResult(bindingContext); // Assert Assert.Null(vpResult); // No key matched } private static ExtensibleModelBindingContext GetBindingContext() { return GetBindingContext(typeof(int)); } private static ExtensibleModelBindingContext GetBindingContext(Type modelType) { return new ExtensibleModelBindingContext { ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType), ModelName = "theModelName" }; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/AcceptAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class AcceptAttributeTest { [Fact] public void DefaultIsEmpty() { Assert.True(String.IsNullOrEmpty(new AcceptAttribute().MimeTypes)); } [Fact] public void ClientRule() { // Arrange var attribute = new AcceptAttribute { MimeTypes = " text/html , application/javascript " }; var provider = new Mock(); var metadata = new ModelMetadata(provider.Object, null, null, typeof(string), "PropertyName"); // Act ModelClientValidationRule clientRule = attribute.GetClientValidationRules(metadata, null).Single(); // Assert Assert.Equal("accept", clientRule.ValidationType); Assert.Equal("The PropertyName field only accepts files with one of the following content types: text/html, application/javascript.", clientRule.ErrorMessage); Assert.Single(clientRule.ValidationParameters); Assert.Equal("text/html,application/javascript", clientRule.ValidationParameters["mimetype"]); } [Fact] public void IsValidTests() { // Arrange var attribute = new AcceptAttribute { MimeTypes = " text/html , application/javascript " }; // Act & Assert Assert.True(attribute.IsValid(null)); // Optional values are always valid Assert.True(attribute.IsValid("text/html")); Assert.True(attribute.IsValid("application/javascript")); Assert.False(attribute.IsValid("text/css")); Assert.False(attribute.IsValid("\0text/html")); // Illegal character } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/AjaxOnlyAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class AjaxOnlyAttributeTest { [Fact] public void IsValidForRequestReturnsFalseIfHeaderNotPresent() { // Arrange AjaxOnlyAttribute attr = new AjaxOnlyAttribute(); ControllerContext controllerContext = GetControllerContext(containsHeader: false); // Act bool isValid = attr.IsValidForRequest(controllerContext, null); // Assert Assert.False(isValid); } [Fact] public void IsValidForRequestReturnsTrueIfHeaderIsPresent() { // Arrange AjaxOnlyAttribute attr = new AjaxOnlyAttribute(); ControllerContext controllerContext = GetControllerContext(containsHeader: true); // Act bool isValid = attr.IsValidForRequest(controllerContext, null); // Assert Assert.True(isValid); } [Fact] public void IsValidForRequestThrowsIfControllerContextIsNull() { // Arrange AjaxOnlyAttribute attr = new AjaxOnlyAttribute(); // Act & assert Assert.ThrowsArgumentNull( delegate { attr.IsValidForRequest(null, null); }, "controllerContext"); } private static ControllerContext GetControllerContext(bool containsHeader) { Mock mockContext = new Mock(); NameValueCollection nvc = new NameValueCollection(); if (containsHeader) { nvc["X-Requested-With"] = "XMLHttpRequest"; } mockContext.Setup(o => o.HttpContext.Request.Headers).Returns(nvc); mockContext.Setup(o => o.HttpContext.Request["X-Requested-With"]).Returns("XMLHttpRequest"); // always assume the request contains this, e.g. as a form value return mockContext.Object; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/AreaHelpersTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class AreaHelpersTest { [Fact] public void GetAreaNameFromAreaRouteCollectionRoute() { // Arrange RouteCollection routes = new RouteCollection(); AreaRegistrationContext context = new AreaRegistrationContext("area_name", routes); Route route = context.MapRoute(null, "the_url"); // Act string areaName = AreaHelpers.GetAreaName(route); // Assert Assert.Equal("area_name", areaName); } [Fact] public void GetAreaNameFromIAreaAssociatedItem() { // Arrange CustomRouteWithArea route = new CustomRouteWithArea(); // Act string areaName = AreaHelpers.GetAreaName(route); // Assert Assert.Equal("area_name", areaName); } [Fact] public void GetAreaNameFromRouteData() { // Arrange RouteData routeData = new RouteData(); routeData.DataTokens["area"] = "area_name"; // Act string areaName = AreaHelpers.GetAreaName(routeData); // Assert Assert.Equal("area_name", areaName); } [Fact] public void GetAreaNameFromRouteDataFallsBackToRoute() { // Arrange RouteCollection routes = new RouteCollection(); AreaRegistrationContext context = new AreaRegistrationContext("area_name", routes); Route route = context.MapRoute(null, "the_url"); RouteData routeData = new RouteData(route, new MvcRouteHandler()); // Act string areaName = AreaHelpers.GetAreaName(routeData); // Assert Assert.Equal("area_name", areaName); } [Fact] public void GetAreaNameReturnsNullIfRouteNotAreaAware() { // Arrange Route route = new Route("the_url", new MvcRouteHandler()); // Act string areaName = AreaHelpers.GetAreaName(route); // Assert Assert.Null(areaName); } private class CustomRouteWithArea : RouteBase, IRouteWithArea { public string Area { get { return "area_name"; } } public override RouteData GetRouteData(HttpContextBase httpContext) { throw new NotImplementedException(); } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { throw new NotImplementedException(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/AsyncManagerExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading; using System.Web.Mvc.Async; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class AsyncManagerExtensionsTest { [Fact] public void RegisterTask_AsynchronousCompletion() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; using (ManualResetEvent waitHandle = new ManualResetEvent(false /* initialState */)) { Func beginDelegate = callback => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); MockAsyncResult asyncResult = new MockAsyncResult(false /* completedSynchronously */); ThreadPool.QueueUserWorkItem(_ => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); callback(asyncResult); waitHandle.Set(); }); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); waitHandle.WaitOne(); // Assert Assert.True(endDelegateWasCalled); Assert.True(syncContext.SendWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } } [Fact] public void RegisterTask_AsynchronousCompletion_SwallowsExceptionsThrownByEndDelegate() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; using (ManualResetEvent waitHandle = new ManualResetEvent(false /* initialState */)) { Func beginDelegate = callback => { MockAsyncResult asyncResult = new MockAsyncResult(false /* completedSynchronously */); ThreadPool.QueueUserWorkItem(_ => { callback(asyncResult); waitHandle.Set(); }); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; throw new Exception("This is a sample exception."); }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); waitHandle.WaitOne(); // Assert Assert.True(endDelegateWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } } [Fact] public void RegisterTask_ResetsOutstandingOperationCountIfBeginMethodThrows() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); Func beginDelegate = cb => { throw new InvalidOperationException("BeginDelegate throws."); }; Action endDelegate = ar => { Assert.True(false, "This should never be called."); }; // Act & assert Assert.Throws( delegate { asyncManager.RegisterTask(beginDelegate, endDelegate); }, "BeginDelegate throws."); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_SynchronousCompletion() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; Func beginDelegate = callback => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); MockAsyncResult asyncResult = new MockAsyncResult(true /* completedSynchronously */); callback(asyncResult); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); // Assert Assert.True(endDelegateWasCalled); Assert.False(syncContext.SendWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_ThrowsIfAsyncManagerIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { AsyncManagerExtensions.RegisterTask(null, _ => null, _ => { }); }, "asyncManager"); } [Fact] public void RegisterTask_ThrowsIfBeginDelegateIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new AsyncManager().RegisterTask(null, _ => { }); }, "beginDelegate"); } [Fact] public void RegisterTask_ThrowsIfEndDelegateIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new AsyncManager().RegisterTask(_ => null, null); }, "endDelegate"); } private class SimpleSynchronizationContext : SynchronizationContext { public bool SendWasCalled; public override void Send(SendOrPostCallback d, object state) { SendWasCalled = true; d(state); } } private class MockAsyncResult : IAsyncResult { private readonly bool _completedSynchronously; public MockAsyncResult(bool completedSynchronously) { _completedSynchronously = completedSynchronously; } public object AsyncState { get { throw new NotImplementedException(); } } public WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { return _completedSynchronously; } } public bool IsCompleted { get { throw new NotImplementedException(); } } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ButtonTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class ButtonTest { [Fact] public void ButtonWithNullNameThrowsArgumentNullException() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); Assert.ThrowsArgumentNull(() => html.Button(null, "text", HtmlButtonType.Button), "name"); } [Fact] public void ButtonRendersBaseAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "buttonText", HtmlButtonType.Reset, "onclickAttr"); Assert.Equal("", result.ToHtmlString()); } [Fact] public void ButtonWithoutOnClickDoesNotRenderOnclickAttribute() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "buttonText", HtmlButtonType.Reset); Assert.Equal("", result.ToHtmlString()); } [Fact] public void ButtonAllowsInnerHtml() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "", HtmlButtonType.Submit, "onclickAttr"); Assert.Equal("", result.ToHtmlString()); } [Fact] public void ButtonRendersExplicitAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "buttonText", HtmlButtonType.Reset, "onclickAttr", new { title = "the-title" }); Assert.Equal("", result.ToHtmlString()); } [Fact] public void ButtonRendersExplicitAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "buttonText", HtmlButtonType.Reset, "onclickAttr", new { foo_bar = "baz" }); Assert.Equal("", result.ToHtmlString()); } [Fact] public void ButtonRendersExplicitDictionaryAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString result = html.Button("nameAttr", "buttonText", HtmlButtonType.Button, "onclickAttr", new RouteValueDictionary(new { title = "the-title" })); Assert.Equal("", result.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ContentTypeAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class ContentTypeAttributeTest { [Fact] public void ContentTypeSetInCtor() { var attr = new ContentTypeAttribute("text/html"); Assert.Equal("text/html", attr.ContentType); } [Fact] public void ContentTypeCtorThrowsArgumentExceptionWhenContentTypeIsNull() { Assert.ThrowsArgumentNullOrEmpty(() => new ContentTypeAttribute(null), "contentType"); } [Fact] public void ExecuteResultSetsContentType() { var mockHttpResponse = new Mock(); var mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.Response).Returns(mockHttpResponse.Object); var mockController = new Mock(); var controllerContext = new ControllerContext(new RequestContext(mockHttpContext.Object, new RouteData()), mockController.Object); var result = new ContentResult { Content = "blah blah" }; var filterContext = new ResultExecutingContext(controllerContext, result); var filter = new ContentTypeAttribute("text/xml"); filter.OnResultExecuting(filterContext); mockHttpResponse.VerifySet(r => r.ContentType = "text/xml"); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ControllerExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class ControllerExtensionsTest { private const string AppPathModifier = MvcHelper.AppPathModifier; [Fact] public void RedirectToAction_DifferentController() { // Act RedirectToRouteResult result = new SampleController().RedirectToAction(x => x.SomeOtherMethod(84)); // Assert Assert.NotNull(result); Assert.Equal("", result.RouteName); Assert.Equal(3, result.RouteValues.Count); Assert.Equal("Different", result.RouteValues["controller"]); Assert.Equal("SomeOtherMethod", result.RouteValues["action"]); Assert.Equal(84, result.RouteValues["someOtherParameter"]); } [Fact] public void RedirectToAction_SameController() { // Act RedirectToRouteResult result = new SampleController().RedirectToAction(x => x.SomeMethod(42)); // Assert Assert.NotNull(result); Assert.Equal("", result.RouteName); Assert.Equal(3, result.RouteValues.Count); Assert.Equal("Sample", result.RouteValues["controller"]); Assert.Equal("SomeMethod", result.RouteValues["action"]); Assert.Equal(42, result.RouteValues["someParameter"]); } [Fact] public void RedirectToAction_ThrowsIfControllerIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { ((SampleController)null).RedirectToAction(x => x.SomeMethod(42)); }, "controller"); } private class SampleController : Controller { public ActionResult SomeMethod(int someParameter) { throw new NotImplementedException(); } } private class DifferentController : Controller { public ActionResult SomeOtherMethod(int someOtherParameter) { throw new NotImplementedException(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/CookieValueProviderFactoryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; using System.Web; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class CookieValueProviderFactoryTest { [Fact] public void GetValueProvider() { // Arrange HttpCookieCollection cookies = new HttpCookieCollection { new HttpCookie("foo", "fooValue"), new HttpCookie("bar.baz", "barBazValue"), new HttpCookie("", "emptyValue"), new HttpCookie(null, "nullValue") }; Mock mockControllerContext = new Mock(); mockControllerContext.Setup(o => o.HttpContext.Request.Cookies).Returns(cookies); CookieValueProviderFactory factory = new CookieValueProviderFactory(); // Act IValueProvider provider = factory.GetValueProvider(mockControllerContext.Object); // Assert Assert.Null(provider.GetValue("")); Assert.True(provider.ContainsPrefix("bar")); Assert.Equal("fooValue", provider.GetValue("foo").AttemptedValue); Assert.Equal(CultureInfo.InvariantCulture, provider.GetValue("foo").Culture); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/CopyAsyncParametersAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class CopyAsyncParametersAttributeTest { [Fact] public void OnActionExecuting_CopiesParametersIfControllerIsAsync() { // Arrange CopyAsyncParametersAttribute attr = new CopyAsyncParametersAttribute(); SampleAsyncController controller = new SampleAsyncController(); ActionExecutingContext filterContext = new ActionExecutingContext { ActionParameters = new Dictionary(StringComparer.OrdinalIgnoreCase), Controller = controller }; filterContext.ActionParameters["foo"] = "fooAction"; filterContext.ActionParameters["bar"] = "barAction"; controller.AsyncManager.Parameters["bar"] = "barAsync"; controller.AsyncManager.Parameters["baz"] = "bazAsync"; // Act attr.OnActionExecuting(filterContext); // Assert Assert.Equal("fooAction", controller.AsyncManager.Parameters["foo"]); Assert.Equal("barAction", controller.AsyncManager.Parameters["bar"]); Assert.Equal("bazAsync", controller.AsyncManager.Parameters["baz"]); } [Fact] public void OnActionExecuting_DoesNothingIfControllerNotAsync() { // Arrange CopyAsyncParametersAttribute attr = new CopyAsyncParametersAttribute(); SampleSyncController controller = new SampleSyncController(); ActionExecutingContext filterContext = new ActionExecutingContext { ActionParameters = new Dictionary(StringComparer.OrdinalIgnoreCase), Controller = controller }; filterContext.ActionParameters["foo"] = "originalFoo"; filterContext.ActionParameters["bar"] = "originalBar"; // Act attr.OnActionExecuting(filterContext); // Assert // If we got this far without crashing, life is good :) } [Fact] public void OnActionExecuting_ThrowsIfFilterContextIsNull() { // Arrange CopyAsyncParametersAttribute attr = new CopyAsyncParametersAttribute(); // Act & assert Assert.ThrowsArgumentNull( delegate { attr.OnActionExecuting(null); }, "filterContext"); } private class SampleSyncController : Controller { } private class SampleAsyncController : AsyncController { } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/CreditCardAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class CreditCardAttributeTest { [Fact] public void ClientRule() { // Arrange var attribute = new CreditCardAttribute(); var provider = new Mock(); var metadata = new ModelMetadata(provider.Object, null, null, typeof(string), "PropertyName"); // Act ModelClientValidationRule clientRule = attribute.GetClientValidationRules(metadata, null).Single(); // Assert Assert.Equal("creditcard", clientRule.ValidationType); Assert.Equal("The PropertyName field is not a valid credit card number.", clientRule.ErrorMessage); Assert.Empty(clientRule.ValidationParameters); } [Fact] public void IsValidTests() { // Arrange var attribute = new CreditCardAttribute(); // Act & Assert Assert.True(attribute.IsValid(null)); // Optional values are always valid Assert.True(attribute.IsValid("0000000000000000")); // Simplest valid value Assert.True(attribute.IsValid("1234567890123452")); // Good checksum Assert.True(attribute.IsValid("1234-5678-9012-3452")); // Good checksum, with dashes Assert.False(attribute.IsValid("0000000000000001")); // Bad checksum Assert.False(attribute.IsValid(0)); // Non-string Assert.False(attribute.IsValid("000%000000000001")); // Non-digit } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/CssExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class CssExtensionsTests { [Fact] public void CssWithoutFileThrowsArgumentNullException() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Assert Assert.ThrowsArgumentNullOrEmpty(() => html.Css(null), "file"); } [Fact] public void CssWithRootedPathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("~/Correct/Path.css"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithRelativePathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("../../Correct/Path.css"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithRelativeCurrentPathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("/Correct/Path.css"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithContentRelativePathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("Correct/Path.css"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithNullMediaTypeRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("Correct/Path.css", null); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithEmptyMediaTypeRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("Correct/Path.css", String.Empty); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithMediaTypeRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("Correct/Path.css", "Print"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithUrlRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("http://ajax.Correct.com/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void CssWithSecureUrlRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Css("https://ajax.Correct.com/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/DeserializeAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Runtime.Serialization; using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.Test { public class DeserializeAttributeTest { [Fact] public void BinderReturnsDeserializedValue() { // Arrange Mock mockSerializer = new Mock(); mockSerializer.Setup(o => o.Deserialize("some-value")).Returns(42); DeserializeAttribute attr = new DeserializeAttribute() { Serializer = mockSerializer.Object }; IModelBinder binder = attr.GetBinder(); ModelBindingContext mbContext = new ModelBindingContext { ModelName = "someKey", ValueProvider = new SimpleValueProvider { { "someKey", "some-value" } } }; // Act object retVal = binder.BindModel(null, mbContext); // Assert Assert.Equal(42, retVal); } [Fact] public void BinderReturnsNullIfValueProviderDoesNotContainKey() { // Arrange DeserializeAttribute attr = new DeserializeAttribute(); IModelBinder binder = attr.GetBinder(); ModelBindingContext mbContext = new ModelBindingContext { ModelName = "someKey", ValueProvider = new SimpleValueProvider() }; // Act object retVal = binder.BindModel(null, mbContext); // Assert Assert.Null(retVal); } [Fact] public void BinderThrowsIfBindingContextIsNull() { // Arrange DeserializeAttribute attr = new DeserializeAttribute(); IModelBinder binder = attr.GetBinder(); // Act & assert Assert.ThrowsArgumentNull( delegate { binder.BindModel(null, null); }, "bindingContext"); } [Fact] public void BinderThrowsIfDataCorrupt() { // Arrange Mock mockSerializer = new Mock(); mockSerializer.Setup(o => o.Deserialize(It.IsAny())).Throws(new SerializationException()); DeserializeAttribute attr = new DeserializeAttribute { Serializer = mockSerializer.Object }; IModelBinder binder = attr.GetBinder(); ModelBindingContext mbContext = new ModelBindingContext { ModelName = "someKey", ValueProvider = new SimpleValueProvider { { "someKey", "This data is corrupted." } } }; // Act & assert Exception exception = Assert.Throws( delegate { binder.BindModel(null, mbContext); }); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/DynamicReflectionObjectTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class DynamicReflectionObjectTest { [Fact] public void NoPropertiesThrows() { // Arrange dynamic dro = DynamicReflectionObject.Wrap(new { }); // Act & Assert Assert.Throws( () => dro.baz, "The property baz doesn't exist. There are no public properties on this object."); } [Fact] public void UnknownPropertyThrows() { // Arrange dynamic dro = DynamicReflectionObject.Wrap(new { foo = 3.4, biff = "Two", bar = 1 }); // Act & Assert Assert.Throws( () => dro.baz, "The property baz doesn't exist. Supported properties are: bar, biff, foo."); } [Fact] public void CanAccessProperties() { // Arrange dynamic dro = DynamicReflectionObject.Wrap(new { foo = "Hello world!", bar = 42 }); // Act & Assert Assert.Equal("Hello world!", dro.foo); Assert.Equal(42, dro.bar); } [Fact] public void CanAccessNestedAnonymousProperties() { // Arrange dynamic dro = DynamicReflectionObject.Wrap(new { foo = new { bar = "Hello world!" } }); // Act & Assert Assert.Equal("Hello world!", dro.foo.bar); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/DynamicViewDataDictionaryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class DynamicViewDataDictionaryTest { // Property-style accessor [Fact] public void Property_UnknownItemReturnsEmptyString() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary(); dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd.Foo; // Assert Assert.Equal(String.Empty, result); } [Fact] public void Property_CanAccessViewDataValues() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary(); vdd["Foo"] = "Value for Foo"; dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd.Foo; // Assert Assert.Equal("Value for Foo", result); } [Fact] public void Property_CanAccessModelProperties() { ViewDataDictionary vdd = new ViewDataDictionary(new { Foo = "Value for Foo" }); dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd.Foo; // Assert Assert.Equal("Value for Foo", result); } // Index-style accessor [Fact] public void Indexer_GuardClauses() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary(); dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act & Assert Assert.Throws( () => { var x = dvdd["foo", "bar"]; }, "DynamicViewDataDictionary only supports single indexers."); Assert.Throws( () => { var x = dvdd[42]; }, "DynamicViewDataDictionary only supports string-based indexers."); } [Fact] public void Indexer_UnknownItemReturnsEmptyString() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary(); dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd["Foo"]; // Assert Assert.Equal(String.Empty, result); } [Fact] public void Indexer_CanAccessViewDataValues() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary(); vdd["Foo"] = "Value for Foo"; dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd["Foo"]; // Assert Assert.Equal("Value for Foo", result); } [Fact] public void Indexer_CanAccessModelProperties() { ViewDataDictionary vdd = new ViewDataDictionary(new { Foo = "Value for Foo" }); dynamic dvdd = DynamicViewDataDictionary.Wrap(vdd); // Act object result = dvdd["Foo"]; // Assert Assert.Equal("Value for Foo", result); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/DynamicViewPageTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class DynamicViewPageTest { // DynamicViewPage [Fact] public void AnonymousObjectsAreWrapped() { // Arrange DynamicViewPage page = new DynamicViewPage(); page.ViewData.Model = new { foo = "Hello world!" }; // Act & Assert Assert.Equal("Microsoft.Web.Mvc.DynamicReflectionObject", page.Model.GetType().FullName); } [Fact] public void NonAnonymousObjectsAreNotWrapped() { // Arrange DynamicViewPage page = new DynamicViewPage(); page.ViewData.Model = "Hello world!"; // Act & Assert Assert.Equal(typeof(string), page.Model.GetType()); } [Fact] public void ViewDataDictionaryIsWrapped() { // Arrange DynamicViewPage page = new DynamicViewPage(); // Act & Assert Assert.Equal("Microsoft.Web.Mvc.DynamicViewDataDictionary", page.ViewData.GetType().FullName); } // DynamicViewPage [Fact] public void Generic_ViewDataDictionaryIsWrapped() { // Arrange DynamicViewPage page = new DynamicViewPage(); // Act & Assert Assert.Equal("Microsoft.Web.Mvc.DynamicViewDataDictionary", page.ViewData.GetType().FullName); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ElementalValueProviderTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class ElementalValueProviderTest { [Fact] public void ContainsPrefix() { // Arrange ElementalValueProvider valueProvider = new ElementalValueProvider("foo", 42, null); // Act & assert Assert.True(valueProvider.ContainsPrefix("foo")); Assert.False(valueProvider.ContainsPrefix("bar")); } [Fact] public void GetValue_NameDoesNotMatch_ReturnsNull() { // Arrange CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); DateTime rawValue = new DateTime(2001, 1, 2); ElementalValueProvider valueProvider = new ElementalValueProvider("foo", rawValue, culture); // Act ValueProviderResult vpResult = valueProvider.GetValue("bar"); // Assert Assert.Null(vpResult); } [Fact] public void GetValue_NameMatches_ReturnsValueProviderResult() { // Arrange CultureInfo culture = CultureInfo.GetCultureInfo("fr-FR"); DateTime rawValue = new DateTime(2001, 1, 2); ElementalValueProvider valueProvider = new ElementalValueProvider("foo", rawValue, culture); // Act ValueProviderResult vpResult = valueProvider.GetValue("FOO"); // Assert Assert.NotNull(vpResult); Assert.Equal(rawValue, vpResult.RawValue); Assert.Equal("02/01/2001 00:00:00", vpResult.AttemptedValue); Assert.Equal(culture, vpResult.Culture); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/EmailAddressAttribueTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class EmailAddressAttribueTest { [Fact] public void ClientRule() { // Arrange var attribute = new EmailAddressAttribute(); var provider = new Mock(); var metadata = new ModelMetadata(provider.Object, null, null, typeof(string), "PropertyName"); // Act ModelClientValidationRule clientRule = attribute.GetClientValidationRules(metadata, null).Single(); // Assert Assert.Equal("email", clientRule.ValidationType); Assert.Equal("The PropertyName field is not a valid e-mail address.", clientRule.ErrorMessage); Assert.Empty(clientRule.ValidationParameters); } [Fact] public void IsValidTests() { // Arrange var attribute = new EmailAddressAttribute(); // Act & Assert Assert.True(attribute.IsValid(null)); // Optional values are always valid Assert.True(attribute.IsValid("joe@contoso.com")); Assert.True(attribute.IsValid("joe%fred@contoso.com")); Assert.False(attribute.IsValid("joe")); Assert.False(attribute.IsValid("joe@")); Assert.False(attribute.IsValid("joe@contoso")); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ExpressionHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq.Expressions; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using ExpressionHelper = Microsoft.Web.Mvc.Internal.ExpressionHelper; namespace Microsoft.Web.Mvc.Test { public class ExpressionHelperTest { [Fact] public void BuildRouteValueDictionary_TargetsAsynchronousAsyncMethod_StripsSuffix() { // Arrange Expression> expr = (c => c.AsynchronousAsync()); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(expr); // Assert Assert.Equal("Asynchronous", rvd["action"]); Assert.Equal("TestAsync", rvd["controller"]); Assert.False(rvd.ContainsKey("area")); } [Fact] public void BuildRouteValueDictionary_TargetsAsynchronousCompletedMethod_Throws() { // Arrange Expression> expr = (c => c.AsynchronousCompleted()); // Act & assert Assert.Throws( delegate { ExpressionHelper.GetRouteValuesFromExpression(expr); }, @"The method 'AsynchronousCompleted' is an asynchronous completion method and cannot be called directly."); } [Fact] public void BuildRouteValueDictionary_TargetsControllerWithAreaAttribute_AddsAreaName() { // Arrange Expression> expr = c => c.Index(); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(expr); // Assert Assert.Equal("Index", rvd["action"]); Assert.Equal("ControllerWithArea", rvd["controller"]); Assert.Equal("the area name", rvd["area"]); } [Fact] public void BuildRouteValueDictionary_TargetsNonActionMethod_Throws() { // Arrange Expression> expr = (c => c.NotAnAction()); // Act & assert Assert.Throws( delegate { ExpressionHelper.GetRouteValuesFromExpression(expr); }, @"The method 'NotAnAction' is marked [NonAction] and cannot be called directly."); } [Fact] public void BuildRouteValueDictionary_TargetsRenamedMethod_UsesNewName() { // Arrange Expression> expr = (c => c.Renamed()); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(expr); // Assert Assert.Equal("NewName", rvd["action"]); Assert.Equal("Test", rvd["controller"]); Assert.False(rvd.ContainsKey("area")); } [Fact] public void BuildRouteValueDictionary_TargetsSynchronousMethodOnAsyncController_ReturnsOriginalName() { // Arrange Expression> expr = (c => c.Synchronous()); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(expr); // Assert Assert.Equal("Synchronous", rvd["action"]); Assert.Equal("TestAsync", rvd["controller"]); Assert.False(rvd.ContainsKey("area")); } [Fact] public void BuildRouteValueDictionaryWithNullExpressionThrowsArgumentNullException() { Assert.ThrowsArgumentNull( () => ExpressionHelper.GetRouteValuesFromExpression(null), "action"); } [Fact] public void BuildRouteValueDictionaryWithNonMethodExpressionThrowsInvalidOperationException() { // Arrange Expression> expression = c => new TestController(); // Act & Assert Assert.Throws( () => ExpressionHelper.GetRouteValuesFromExpression(expression), "Expression must be a method call." + Environment.NewLine + "Parameter name: action"); } [Fact] public void BuildRouteValueDictionaryWithoutControllerSuffixThrowsInvalidOperationException() { // Arrange Expression> index = (c => c.Index(123)); // Act & Assert Assert.Throws( () => ExpressionHelper.GetRouteValuesFromExpression(index), "Controller name must end in 'Controller'." + Environment.NewLine + "Parameter name: action"); } [Fact] public void BuildRouteValueDictionaryWithControllerBaseClassThrowsInvalidOperationException() { // Arrange Expression> index = (c => c.Dispose()); // Act & Assert Assert.Throws( () => ExpressionHelper.GetRouteValuesFromExpression(index), "Cannot route to class named 'Controller'." + Environment.NewLine + "Parameter name: action"); } [Fact] public void BuildRouteValueDictionaryAddsControllerNameToDictionary() { // Arrange Expression> index = (c => c.Index(123)); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(index); // Assert Assert.Equal("Test", rvd["Controller"]); } [Fact] public void BuildRouteValueDictionaryFromExpressionReturnsCorrectDictionary() { // Arrange Expression> index = (c => c.Index(123)); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(index); // Assert Assert.Equal("Test", rvd["Controller"]); Assert.Equal("Index", rvd["Action"]); Assert.Equal(123, rvd["page"]); } [Fact] public void BuildRouteValueDictionaryFromNonConstantExpressionReturnsCorrectDictionary() { // Arrange Expression> index = (c => c.About(Foo)); // Act RouteValueDictionary rvd = ExpressionHelper.GetRouteValuesFromExpression(index); // Assert Assert.Equal("Test", rvd["Controller"]); Assert.Equal("About", rvd["Action"]); Assert.Equal("FooValue", rvd["s"]); } [Fact] public void GetInputNameFromPropertyExpressionReturnsPropertyName() { // Arrange Expression> expression = m => m.IntProperty; // Act string name = ExpressionHelper.GetInputName(expression); // Assert Assert.Equal("IntProperty", name); } [Fact] public void GetInputNameFromPropertyWithMethodCallExpressionReturnsPropertyName() { // Arrange Expression> expression = m => m.IntProperty.ToString(); // Act string name = ExpressionHelper.GetInputName(expression); // Assert Assert.Equal("IntProperty", name); } [Fact] public void GetInputNameFromPropertyWithTwoMethodCallExpressionReturnsPropertyName() { // Arrange Expression> expression = m => m.IntProperty.ToString().ToUpper(); // Act string name = ExpressionHelper.GetInputName(expression); // Assert Assert.Equal("IntProperty", name); } [Fact] public void GetInputNameFromExpressionWithTwoPropertiesUsesWholeExpression() { // Arrange Expression> expression = m => m.StringProperty.Length; // Act string name = ExpressionHelper.GetInputName(expression); // Assert Assert.Equal("StringProperty.Length", name); } public class TestController : Controller { public ActionResult Index(int page) { return null; } public string About(string s) { return "The value is " + s; } [ActionName("NewName")] public void Renamed() { } [NonAction] public void NotAnAction() { } } public class TestAsyncController : AsyncController { public void Synchronous() { } public void AsynchronousAsync() { } public void AsynchronousCompleted() { } } public string Foo { get { return "FooValue"; } } public class TestControllerNot : Controller { public ActionResult Index(int page) { return null; } } [ActionLinkArea("the area name")] public class ControllerWithAreaController : Controller { public ActionResult Index() { return null; } } public class TestModel { public int IntProperty { get; set; } public string StringProperty { get; set; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/FileExtensionsAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class FileExtensionsAttributeTest { [Fact] public void DefaultExtensions() { Assert.Equal("png,jpg,jpeg,gif", new FileExtensionsAttribute().Extensions); } [Fact] public void ClientRule() { // Arrange var attribute = new FileExtensionsAttribute { Extensions = " FoO, .bar,baz " }; var provider = new Mock(); var metadata = new ModelMetadata(provider.Object, null, null, typeof(string), "PropertyName"); // Act ModelClientValidationRule clientRule = attribute.GetClientValidationRules(metadata, null).Single(); // Assert Assert.Equal("extension", clientRule.ValidationType); Assert.Equal("The PropertyName field only accepts files with the following extensions: .foo, .bar, .baz", clientRule.ErrorMessage); Assert.Single(clientRule.ValidationParameters); Assert.Equal("foo,bar,baz", clientRule.ValidationParameters["extension"]); } [Fact] public void IsValidTests() { // Arrange var attribute = new FileExtensionsAttribute(); // Act & Assert Assert.True(attribute.IsValid(null)); // Optional values are always valid Assert.True(attribute.IsValid("foo.png")); Assert.True(attribute.IsValid("foo.jpeg")); Assert.True(attribute.IsValid("foo.jpg")); Assert.True(attribute.IsValid("foo.gif")); Assert.True(attribute.IsValid(@"C:\Foo\baz.jpg")); Assert.False(attribute.IsValid("foo")); Assert.False(attribute.IsValid("foo.png.pif")); Assert.False(attribute.IsValid(@"C:\foo.png\bar")); Assert.False(attribute.IsValid("\0foo.png")); // Illegal character } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/FormExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class FormExtensionsTest { internal const string AppPathModifier = "/$(SESSION)"; [Fact] public void FormWithPostAction() { // Arrange StringWriter writer; HtmlHelper htmlHelper = GetFormHelper(out writer); // Act IDisposable formDisposable = htmlHelper.BeginForm(action => action.About()); formDisposable.Dispose(); // Assert Assert.Equal(@"
", writer.ToString()); } [Fact] public void FormWithPostActionAndObjectAttributes() { // Arrange StringWriter writer; HtmlHelper htmlHelper = GetFormHelper(out writer); // Act IDisposable formDisposable = htmlHelper.BeginForm(action => action.About(), FormMethod.Get, new { baz = "baz" }); formDisposable.Dispose(); // Assert Assert.Equal(@"
", writer.ToString()); } [Fact] public void FormWithPostActionAndObjectAttributesWithUnderscores() { // Arrange StringWriter writer; HtmlHelper htmlHelper = GetFormHelper(out writer); // Act IDisposable formDisposable = htmlHelper.BeginForm(action => action.About(), FormMethod.Get, new { foo_baz = "baz" }); formDisposable.Dispose(); // Assert Assert.Equal(@"
", writer.ToString()); } public class FormController : Controller { public ActionResult About() { return RedirectToAction("foo"); } } private static HtmlHelper GetFormHelper(out StringWriter writer) { Mock mockHttpRequest = new Mock(); mockHttpRequest.Setup(r => r.Url).Returns(new Uri("http://www.contoso.com/some/path")); Mock mockHttpResponse = new Mock(MockBehavior.Strict); mockHttpResponse.Setup(r => r.ApplyAppPathModifier(It.IsAny())).Returns(r => AppPathModifier + r); Mock mockHttpContext = new Mock(); mockHttpContext.Setup(c => c.Request).Returns(mockHttpRequest.Object); mockHttpContext.Setup(c => c.Response).Returns(mockHttpResponse.Object); RouteCollection rt = new RouteCollection(); rt.Add(new Route("{controller}/{action}/{id}", null) { Defaults = new RouteValueDictionary(new { id = "defaultid" }) }); rt.Add("namedroute", new Route("named/{controller}/{action}/{id}", null) { Defaults = new RouteValueDictionary(new { id = "defaultid" }) }); RouteData rd = new RouteData(); rd.Values.Add("controller", "home"); rd.Values.Add("action", "oldaction"); Mock mockViewContext = new Mock(); mockViewContext.Setup(c => c.HttpContext).Returns(mockHttpContext.Object); mockViewContext.Setup(c => c.RouteData).Returns(rd); writer = new StringWriter(); mockViewContext.Setup(c => c.Writer).Returns(writer); HtmlHelper helper = new HtmlHelper( mockViewContext.Object, new Mock().Object, rt); return helper; } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ImageExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class ImageExtensionsTest { [Fact] public void ImageWithEmptyRelativeUrlThrowsArgumentNullException() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); Assert.ThrowsArgumentNullOrEmpty(() => html.Image(null), "imageRelativeUrl"); } [Fact] public void ImageStaticWithEmptyRelativeUrlThrowsArgumentNullException() { Assert.ThrowsArgumentNullOrEmpty(() => ImageExtensions.Image((string)null, "alt", null), "imageUrl"); } [Fact] public void ImageWithRelativeUrlRendersProperImageTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg"); // NOTE: Although XHTML requires an alt tag, we don't construct one for you. Specify it yourself. Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithWithAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", new { foo_bar = "baz" }); Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltValueRendersImageWithAltTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", "this is an alt value"); Assert.Equal("\"this", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltValueInObjectDictionaryRendersImageWithAltAndTitleTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", new { alt = "this is an alt value" }); Assert.Equal("\"this", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltValueHtmlAttributeEncodesAltTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", @"<"">"); Assert.Equal("\"<"\" src=\"/system/web/mvc.jpg\" title=\"<">\" />", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltValueInObjectDictionaryHtmlAttributeEncodesAltTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", new { alt = "this is an alt value" }); Assert.Equal("\"this", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltSpecifiedAndInDictionaryRendersExplicit() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", "specified-alt", new { alt = "object-dictionary-alt" }); Assert.Equal("\"object-dictionary-alt\"", imageResult.ToHtmlString()); } [Fact] public void ImageWithAltAndAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", "specified-alt", new { foo_bar = "baz" }); Assert.Equal("\"specified-alt\"", imageResult.ToHtmlString()); } [Fact] public void ImageWithSrcSpecifiedAndInDictionaryRendersExplicit() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", new { src = "explicit.jpg" }); Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithOtherAttributesRendersThoseAttributesCaseSensitively() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", new { width = 100, Height = 200 }); Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithUrlAndDictionaryRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); var attributes = new RouteValueDictionary(new { width = 125 }); MvcHtmlString imageResult = html.Image("/system/web/mvc.jpg", attributes); Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithTildePathAndAppPathResolvesCorrectly() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary(), "/app"); MvcHtmlString imageResult = html.Image("~/system/web/mvc.jpg"); Assert.Equal("", imageResult.ToHtmlString()); } [Fact] public void ImageWithTildePathWithoutAppPathResolvesCorrectly() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary(), "/"); MvcHtmlString imageResult = html.Image("~/system/web/mvc.jpg"); Assert.Equal("", imageResult.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/MailToExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class MailToExtensionsTest { [Fact] public void MailToWithoutEmailThrowsArgumentNullException() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); Assert.ThrowsArgumentNull(() => html.Mailto("link text", null), "emailAddress"); } [Fact] public void MailToWithoutLinkTextThrowsArgumentNullException() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); Assert.ThrowsArgumentNull(() => html.Mailto(null, "somebody@example.com"), "linkText"); } [Fact] public void MailToWithLinkTextAndEmailRendersProperElement() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com"); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithLinkTextEmailAndHtmlAttributesRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", new { title = "this is a test" }); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithLinkTextEmailAndHtmlAttributesDictionaryRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", new RouteValueDictionary(new { title = "this is a test" })); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithSubjectAndHtmlAttributesRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", "The subject", new { title = "this is a test" }); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithSubjectAndHtmlAttributesDictionaryRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", "The subject", new RouteValueDictionary(new { title = "this is a test" })); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToAttributeEncodesEmail() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "te\">st@example.com"); Assert.Equal("st@example.com\">This is a test", result.ToHtmlString()); } [Fact] public void MailToWithMultipleRecipientsRendersWithCommas() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "te\">st@example.com,test2@example.com"); Assert.Equal("st@example.com,test2@example.com\">This is a test", result.ToHtmlString()); } [Fact] public void MailToWithSubjectAppendsSubjectQuery() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", "This is the subject"); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithCopyOnlyAppendsCopyQuery() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); MvcHtmlString result = html.Mailto("This is a test", "test@example.com", null, null, "cctest@example.com", null, null); Assert.Equal("This is a test", result.ToHtmlString()); } [Fact] public void MailToWithMultipartBodyRendersProperMailtoEncoding() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); string body = "Line one" + Environment.NewLine + "Line two" + Environment.NewLine + "Line three"; MvcHtmlString result = html.Mailto("email me", "test@example.com", null, body, null, null, null); Assert.Equal("email me", result.ToHtmlString()); } [Fact] public void MailToWithAllValuesProvidedRendersCorrectTag() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); string body = "Line one" + Environment.NewLine + "Line two" + Environment.NewLine + "Line three"; MvcHtmlString result = html.Mailto("email me", "test@example.com", "the subject", body, "cc@example.com", "bcc@example.com", new { title = "email test" }); string expected = @"email me"; Assert.Equal(expected, result.ToHtmlString()); } [Fact] public void MailToWithAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); string body = "Line one" + Environment.NewLine + "Line two" + Environment.NewLine + "Line three"; MvcHtmlString result = html.Mailto("email me", "test@example.com", "the subject", body, "cc@example.com", "bcc@example.com", new { foo_bar = "baz" }); string expected = @"email me"; Assert.Equal(expected, result.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ModelCopierTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class ModelCopierTest { [Fact] public void CopyCollection_FromIsNull_DoesNothing() { // Arrange int[] from = null; List to = new List { 1, 2, 3 }; // Act ModelCopier.CopyCollection(from, to); // Assert Assert.Equal(new[] { 1, 2, 3 }, to.ToArray()); } [Fact] public void CopyCollection_ToIsImmutable_DoesNothing() { // Arrange List from = new List { 1, 2, 3 }; ICollection to = new ReadOnlyCollection(new[] { 4, 5, 6 }); // Act ModelCopier.CopyCollection(from, to); // Assert Assert.Equal(new[] { 1, 2, 3 }, from.ToArray()); Assert.Equal(new[] { 4, 5, 6 }, to.ToArray()); } [Fact] public void CopyCollection_ToIsMmutable_ClearsAndCopies() { // Arrange List from = new List { 1, 2, 3 }; ICollection to = new List { 4, 5, 6 }; // Act ModelCopier.CopyCollection(from, to); // Assert Assert.Equal(new[] { 1, 2, 3 }, from.ToArray()); Assert.Equal(new[] { 1, 2, 3 }, to.ToArray()); } [Fact] public void CopyCollection_ToIsNull_DoesNothing() { // Arrange List from = new List { 1, 2, 3 }; List to = null; // Act ModelCopier.CopyCollection(from, to); // Assert Assert.Equal(new[] { 1, 2, 3 }, from.ToArray()); } [Fact] public void CopyModel_ExactTypeMatch_Copies() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); Assert.Equal(21, to.TheProperty); } [Fact] public void CopyModel_FromIsNull_DoesNothing() { // Arrange GenericModel from = null; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(42, to.TheProperty); } [Fact] public void CopyModel_LiftedTypeMatch_ActualValueIsNotNull_Copies() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); Assert.Equal(21, to.TheProperty); } [Fact] public void CopyModel_LiftedTypeMatch_ActualValueIsNull_DoesNothing() { // Arrange GenericModel from = new GenericModel { TheProperty = null }; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Null(from.TheProperty); Assert.Equal(42, to.TheProperty); } [Fact] public void CopyModel_NoTypeMatch_DoesNothing() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); Assert.Equal(42, to.TheProperty); } [Fact] public void CopyModel_SubclassedTypeMatch_Copies() { // Arrange string originalModel = "Hello, world!"; GenericModel from = new GenericModel { TheProperty = originalModel }; GenericModel to = new GenericModel { TheProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Same(originalModel, from.TheProperty); Assert.Same(originalModel, to.TheProperty); } [Fact] public void CopyModel_ToDoesNotContainProperty_DoesNothing() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; OtherGenericModel to = new OtherGenericModel { SomeOtherProperty = 42 }; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); Assert.Equal(42, to.SomeOtherProperty); } [Fact] public void CopyModel_ToIsNull_DoesNothing() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; GenericModel to = null; // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); } [Fact] public void CopyModel_ToIsReadOnly_DoesNothing() { // Arrange GenericModel from = new GenericModel { TheProperty = 21 }; ReadOnlyGenericModel to = new ReadOnlyGenericModel(42); // Act ModelCopier.CopyModel(from, to); // Assert Assert.Equal(21, from.TheProperty); Assert.Equal(42, to.TheProperty); } private class GenericModel { public T TheProperty { get; set; } } private class OtherGenericModel { public T SomeOtherProperty { get; set; } } private class ReadOnlyGenericModel { public ReadOnlyGenericModel(T propertyValue) { TheProperty = propertyValue; } public T TheProperty { get; private set; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/MvcSerializerTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Runtime.Serialization; using System.Web.Security; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class MvcSerializerTest { [Fact] public void DeserializeThrowsIfSerializedValueIsCorrupt() { // Arrange IMachineKey machineKey = new MockMachineKey(); // Act & assert Exception exception = Assert.Throws( delegate { MvcSerializer.Deserialize("This is a corrupted value.", machineKey); }, @"Deserialization failed. Verify that the data is being deserialized using the same SerializationMode with which it was serialized. Otherwise see the inner exception."); Assert.NotNull(exception.InnerException); } [Fact] public void DeserializeThrowsIfSerializedValueIsEmpty() { // Arrange MvcSerializer serializer = new MvcSerializer(); // Act & assert Assert.ThrowsArgumentNullOrEmpty( delegate { serializer.Deserialize(""); }, "serializedValue"); } [Fact] public void DeserializeThrowsIfSerializedValueIsNull() { // Arrange MvcSerializer serializer = new MvcSerializer(); // Act & assert Assert.ThrowsArgumentNullOrEmpty( delegate { serializer.Deserialize(null); }, "serializedValue"); } [Fact] public void SerializeAllowsNullValues() { // Arrange IMachineKey machineKey = new MockMachineKey(); // Act string serializedValue = MvcSerializer.Serialize(null, machineKey); // Assert Assert.Equal(@"Microsoft.Web.Mvc.MvcSerializer.v1-dwdhbnlUeXBlLgNuaWyGCQF6M2h0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vMjAwMy8xMC9TZXJpYWxpemF0aW9uLwkBaSlodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZQE=", serializedValue); } [Fact] public void SerializeAndDeserializeRoundTripsValue() { // Arrange IMachineKey machineKey = new MockMachineKey(); // Act string serializedValue = MvcSerializer.Serialize(42, machineKey); object deserializedValue = MvcSerializer.Deserialize(serializedValue, machineKey); // Assert Assert.Equal(42, deserializedValue); } private sealed class MockMachineKey : IMachineKey { public byte[] Unprotect(string protectedData, params string[] purposes) { string optionString = purposes[0].ToString(); if (protectedData.StartsWith(optionString, StringComparison.Ordinal)) { protectedData = protectedData.Substring(optionString.Length + 1); } else { throw new Exception("Corrupted data."); } return Convert.FromBase64String(protectedData); } public string Protect(byte[] userData, params string[] purposes) { return purposes[0].ToString() + "-" + Convert.ToBase64String(userData); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/RadioExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class RadioExtensionsTest { [Fact] public void RadioButtonListNothingSelected() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(false)); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListItemSelected() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(true)); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListItemSelectedWithValueFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary(new { foolist = "bar" })); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(false)); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithObjectAttributes() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(true), new { attr1 = "value1" }); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithObjectAttributesWithUnderscores() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(true), new { foo_bar = "baz" }); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithDictionaryAttributes() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(true), new RouteValueDictionary(new { attr1 = "value1" })); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListNothingSelectedWithSelectListFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(false)); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList"); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListItemSelectedWithSelectListFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList"); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithObjectAttributesWithSelectListFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", new { attr1 = "value1" }); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithObjectAttributesWithUnderscoresWithSelectListFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", new { foo_bar = "baz" }); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithDictionaryAttributesWithSelectListFromViewData() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act MvcHtmlString[] html = htmlHelper.RadioButtonList("FooList", new RouteValueDictionary(new { attr1 = "value1" })); // Assert Assert.Equal(@"", html[0].ToHtmlString()); Assert.Equal(@"", html[1].ToHtmlString()); Assert.Equal(@"", html[2].ToHtmlString()); } [Fact] public void RadioButtonListWithDictionaryAttributesWithWrongSelectListName() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act / Assert Assert.Throws(() => htmlHelper.RadioButtonList("WrongFooList", new RouteValueDictionary(new { attr1 = "value1" }))); } [Fact] public void RadioButtonListWithDictionaryAttributesWithInvalidSelectListFromViewData() { // Arrange ViewDataDictionary viewData = new ViewDataDictionary(); viewData["FooList"] = "FOOBAR3"; HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(viewData); // Act / Assert Assert.Throws(() => htmlHelper.RadioButtonList("FooList", new RouteValueDictionary(new { attr1 = "value1" }))); } [Fact] public void RadioButtonListWithDictionaryAttributesWithNullSelectListNameFromSelectList() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); SelectList selectList = GetRadioButtonListData(true); // Act / Assert Assert.Throws(() => htmlHelper.RadioButtonList("", selectList, new RouteValueDictionary(new { attr1 = "value1" }))); } [Fact] public void RadioButtonListWithDictionaryAttributesWithSelectListNameAndNullSelectList() { // Arrange HtmlHelper htmlHelper = MvcHelper.GetHtmlHelper(GetRadioButtonListViewData(true)); // Act / Assert Assert.Throws(() => htmlHelper.RadioButtonList("FooBar", null, new RouteValueDictionary(new { attr1 = "value1" }))); } private static SelectList GetRadioButtonListData(bool selectBaz) { List list = new List(); list.Add(new RadioItem { Text = "text-foo", Value = "foo" }); list.Add(new RadioItem { Text = "text-bar", Value = "bar" }); list.Add(new RadioItem { Text = "text-baz", Value = "baz" }); return new SelectList(list, "value", "TEXT", selectBaz ? "baz" : "something-else"); } private static ViewDataDictionary GetRadioButtonListViewData(bool selectBaz) { ViewDataDictionary viewData = new ViewDataDictionary(); viewData["FooList"] = GetRadioButtonListData(selectBaz); return viewData; } private class RadioItem { public string Text { get; set; } public string Value { get; set; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ReaderWriterCacheTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class ReaderWriterCacheTest { [Fact] public void PublicFetchOrCreateItemCreatesItemIfNotAlreadyInCache() { // Arrange ReaderWriterCacheHelper helper = new ReaderWriterCacheHelper(); Dictionary cache = helper.PublicCache; // Act string item = helper.PublicFetchOrCreateItem(42, () => "new"); // Assert Assert.Equal("new", cache[42]); Assert.Equal("new", item); } [Fact] public void PublicFetchOrCreateItemReturnsExistingItemIfFound() { // Arrange ReaderWriterCacheHelper helper = new ReaderWriterCacheHelper(); Dictionary cache = helper.PublicCache; helper.PublicCache[42] = "original"; // Act string item = helper.PublicFetchOrCreateItem(42, () => "new"); // Assert Assert.Equal("original", cache[42]); Assert.Equal("original", item); } [Fact] public void PublicFetchOrCreateItemReturnsFirstItemIfTwoThreadsUpdateCacheSimultaneously() { // Arrange ReaderWriterCacheHelper helper = new ReaderWriterCacheHelper(); Dictionary cache = helper.PublicCache; Func creator = delegate { // fake a second thread coming along when we weren't looking string firstItem = helper.PublicFetchOrCreateItem(42, () => "original"); Assert.Equal("original", cache[42]); Assert.Equal("original", firstItem); return "new"; }; // Act string secondItem = helper.PublicFetchOrCreateItem(42, creator); // Assert Assert.Equal("original", cache[42]); Assert.Equal("original", secondItem); } private class ReaderWriterCacheHelper : ReaderWriterCache { public Dictionary PublicCache { get { return Cache; } } public TValue PublicFetchOrCreateItem(TKey key, Func creator) { return FetchOrCreateItem(key, creator); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/RenderActionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Reflection; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.Test { public class RenderActionTest { [Fact] public void RenderActionUsingExpressionWithParametersInViewContextRendersCorrectly() { // Arrange Func requestContextAccessor; HtmlHelper html = GetHtmlHelper(out requestContextAccessor); html.ViewContext.RouteData.Values.Add("stuff", "42"); // Act html.RenderAction(c => c.Stuff()); RequestContext requestContext = requestContextAccessor(); // Assert Assert.NotNull(requestContext); Assert.Equal("Test", requestContext.RouteData.Values["controller"]); Assert.Equal("Stuff", requestContext.RouteData.Values["action"]); Assert.Equal("42", requestContext.RouteData.Values["stuff"]); } [Fact] public void RenderActionUsingExpressionRendersCorrectly() { // Arrange Func requestContextAccessor; HtmlHelper html = GetHtmlHelper(out requestContextAccessor); // Act html.RenderAction(c => c.About(76)); RequestContext requestContext = requestContextAccessor(); // Assert Assert.NotNull(requestContext); Assert.Equal("Test", requestContext.RouteData.Values["controller"]); Assert.Equal("About", requestContext.RouteData.Values["action"]); Assert.Equal(76, requestContext.RouteData.Values["page"]); } [Fact] public void RenderRouteWithNullRouteValueDictionaryThrowsException() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary(), "/"); Assert.ThrowsArgumentNull(() => html.RenderRoute(null), "routeValues"); } [Fact] public void RenderRouteWithActionAndControllerSpecifiedRendersCorrectAction() { // Arrange Func requestContextAccessor; HtmlHelper html = GetHtmlHelper(out requestContextAccessor); // Act html.RenderRoute(new RouteValueDictionary(new { action = "Index", controller = "Test" })); RequestContext requestContext = requestContextAccessor(); // Assert Assert.NotNull(requestContext); Assert.Equal("Test", requestContext.RouteData.Values["controller"]); Assert.Equal("Index", requestContext.RouteData.Values["action"]); } private static HtmlHelper GetHtmlHelper(out Func requestContextAccessor) { RequestContext requestContext = null; HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary(), "/"); html.RouteCollection.MapRoute(null, "{*dummy}"); Mock.Get(html.ViewContext.HttpContext) .Setup(o => o.Server.Execute(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((_h, _w, _pf) => { MvcHandler mvcHandler = _h.GetType().GetProperty("InnerHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_h, null) as MvcHandler; requestContext = mvcHandler.RequestContext; }); requestContextAccessor = () => requestContext; return html; } public class TestController : Controller { public string Index() { return "It Worked!"; } public string About(int page) { return "This is page #" + page; } public string Stuff() { string stuff = ControllerContext.RouteData.Values["stuff"] as string; return "Argument was " + stuff; } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ScriptExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.Test { public class ScriptExtensionsTest { [Fact] public void ScriptWithoutReleaseFileThrowsArgumentNullException() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Assert Assert.ThrowsArgumentNullOrEmpty(() => html.Script(null, "file"), "releaseFile"); } [Fact] public void ScriptWithoutDebugFileThrowsArgumentNullException() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Assert Assert.ThrowsArgumentNullOrEmpty(() => html.Script("File", null), "debugFile"); } [Fact] public void ScriptWithRootedPathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("~/Correct/Path.js", "~/Correct/Debug/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithRelativePathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("../../Correct/Path.js", "../../Correct/Debug/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithRelativeCurrentPathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("/Correct/Path.js", "/Correct/Debug/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithScriptRelativePathRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("Correct/Path.js", "Correct/Debug/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithUrlRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("http://ajax.Correct.com/Path.js", "http://ajax.Debug.com/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithSecureUrlRendersProperElement() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); // Act MvcHtmlString result = html.Script("https://ajax.Correct.com/Path.js", "https://ajax.Debug.com/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } [Fact] public void ScriptWithDebuggingOnUsesDebugUrl() { // Arrange HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary()); Mock.Get(html.ViewContext.HttpContext).Setup(v => v.IsDebuggingEnabled).Returns(true); // Act MvcHtmlString result = html.Script("Correct/Path.js", "Correct/Debug/Path.js"); // Assert Assert.Equal("", result.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/SerializationExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; using Moq; namespace Microsoft.Web.Mvc.Test { public class SerializationExtensionsTest { [Fact] public void SerializeFromProvidedValueOverridesViewData() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary { { "someKey", 42 } }; HtmlHelper helper = MvcHelper.GetHtmlHelper(vdd); Mock mockSerializer = new Mock(); mockSerializer.Setup(o => o.Serialize("Hello!")).Returns("some-value"); // Act MvcHtmlString htmlString = helper.Serialize("someKey", "Hello!", mockSerializer.Object); // Assert Assert.Equal(@"", htmlString.ToHtmlString()); } [Fact] public void SerializeFromViewData() { // Arrange ViewDataDictionary vdd = new ViewDataDictionary { { "someKey", 42 } }; HtmlHelper helper = MvcHelper.GetHtmlHelper(vdd); Mock mockSerializer = new Mock(); mockSerializer.Setup(o => o.Serialize(42)).Returns("some-other-value"); // Act MvcHtmlString htmlString = helper.Serialize("someKey", mockSerializer.Object); // Assert Assert.Equal(@"", htmlString.ToHtmlString()); } [Fact] public void SerializeThrowsIfHtmlHelperIsNull() { Assert.ThrowsArgumentNull( delegate { SerializationExtensions.Serialize(null, "someName"); }, "htmlHelper"); } [Fact] public void SerializeThrowsIfNameIsEmpty() { // Arrange HtmlHelper helper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); Assert.ThrowsArgumentNullOrEmpty( delegate { helper.Serialize(""); }, "name"); } [Fact] public void SerializeThrowsIfNameIsNull() { // Arrange HtmlHelper helper = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); Assert.ThrowsArgumentNullOrEmpty( delegate { helper.Serialize(null); }, "name"); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ServerVariablesValueProviderFactoryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Globalization; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class ServerVariablesValueProviderFactoryTest { [Fact] public void GetValueProvider() { // Arrange NameValueCollection serverVars = new NameValueCollection { { "foo", "fooValue" }, { "bar.baz", "barBazValue" } }; Mock mockControllerContext = new Mock(); mockControllerContext.Setup(o => o.HttpContext.Request.ServerVariables).Returns(serverVars); ServerVariablesValueProviderFactory factory = new ServerVariablesValueProviderFactory(); // Act IValueProvider provider = factory.GetValueProvider(mockControllerContext.Object); // Assert Assert.True(provider.ContainsPrefix("bar")); Assert.Equal("fooValue", provider.GetValue("foo").AttemptedValue); Assert.Equal(CultureInfo.InvariantCulture, provider.GetValue("foo").Culture); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/SessionValueProviderFactoryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Web; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class SessionValueProviderFactoryTest { [Fact] public void GetValueProvider() { // Arrange Dictionary backingStore = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "foo", "fooValue" }, { "bar.baz", "barBazValue" } }; MockSessionState session = new MockSessionState(backingStore); Mock mockControllerContext = new Mock(); mockControllerContext.Setup(o => o.HttpContext.Session).Returns(session); SessionValueProviderFactory factory = new SessionValueProviderFactory(); // Act IValueProvider provider = factory.GetValueProvider(mockControllerContext.Object); // Assert Assert.True(provider.ContainsPrefix("bar")); Assert.Equal("fooValue", provider.GetValue("foo").AttemptedValue); Assert.Equal(CultureInfo.InvariantCulture, provider.GetValue("foo").Culture); } private sealed class MockSessionState : HttpSessionStateBase { private readonly IDictionary _backingStore; public MockSessionState(IDictionary backingStore) { _backingStore = backingStore; } public override object this[string name] { get { return _backingStore[name]; } set { _backingStore[name] = value; } } public override IEnumerator GetEnumerator() { return _backingStore.Keys.GetEnumerator(); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/SkipBindingAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class SkipBindingAttributeTest { [Fact] public void GetBinderReturnsModelBinderWhichReturnsNull() { // Arrange CustomModelBinderAttribute attr = new SkipBindingAttribute(); IModelBinder binder = attr.GetBinder(); // Act object result = binder.BindModel(null, null); // Assert Assert.Null(result); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/SubmitButtonExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class SubmitButtonExtensionsTest { [Fact] public void SubmitButtonRendersWithJustTypeAttribute() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton(); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton(null, "blah", new { foo_bar = "baz" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameRendersButtonWithNameAttribute() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("button-name"); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithIdDifferentFromNameRendersButtonWithId() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("button-name", "blah", new { id = "foo" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameAndTextRendersAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("button-name", "button-text"); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameAndValueRendersBothAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("button-name", "button-value", new { id = "button-id" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameAndIdRendersBothAttributesCorrectly() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("button-name", "button-value", new { id = "button-id" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithTypeAttributeRendersCorrectType() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("specified-name", "button-value", new { type = "not-submit" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameAndValueSpecifiedAndPassedInAsAttributeChoosesSpecified() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitButton("specified-name", "button-value", new RouteValueDictionary(new { name = "name-attribute-value", value = "value-attribute" })); Assert.Equal("", button.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/SubmitImageExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.Mvc; using System.Web.Routing; using Microsoft.TestCommon; using Microsoft.Web.UnitTestUtil; namespace Microsoft.Web.Mvc.Test { public class SubmitImageExtensionsTest { [Fact] public void SubmitImageWithEmptyImageSrcThrowsArgumentNullException() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); Assert.ThrowsArgumentNull(() => html.SubmitImage("name", null), "imageSrc"); } [Fact] public void SubmitImageWithAttributesWithUnderscores() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitImage("specified-name", "/mvc.jpg", new { foo_bar = "baz" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitImageWithTypeAttributeRendersExplicitTypeAttribute() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitImage("specified-name", "/mvc.jpg", new { type = "not-image" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitImageWithNameAndImageUrlRendersNameAndSrcAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitImage("button-name", "/mvc.gif"); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitImageWithImageUrlStartingWithTildeRendersAppPath() { HtmlHelper html = MvcHelper.GetHtmlHelperWithPath(new ViewDataDictionary(), "/app"); MvcHtmlString button = html.SubmitImage("button-name", "~/mvc.gif"); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitImageWithNameAndIdRendersBothAttributesCorrectly() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitImage("button-name", "/mvc.png", new { id = "button-id" }); Assert.Equal("", button.ToHtmlString()); } [Fact] public void SubmitButtonWithNameAndValueSpecifiedAndPassedInAsAttributeChoosesExplicitAttributes() { HtmlHelper html = MvcHelper.GetHtmlHelper(new ViewDataDictionary()); MvcHtmlString button = html.SubmitImage("specified-name", "/specified-src.bmp", new RouteValueDictionary(new { name = "name-attribute", src = "src-attribute" })); Assert.Equal("", button.ToHtmlString()); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/TempDataValueProviderFactoryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class TempDataValueProviderFactoryTest { [Fact] public void GetValueProvider_CorrectlyRetainsOrRemovesKeys() { // Arrange string[] expectedRetainedKeys = new[] { "retainMe" }; TempDataDictionary tempData = new TempDataDictionary { { "retainMe", "retainMeValue" }, { "removeMe", "removeMeValue" }, { "previouslyRemoved", "previouslyRemovedValue" } }; object dummy = tempData["previouslyRemoved"]; // mark value for removal ControllerContext controllerContext = GetControllerContext(tempData); TempDataValueProviderFactory factory = new TempDataValueProviderFactory(); // Act IValueProvider valueProvider = factory.GetValueProvider(controllerContext); ValueProviderResult nonExistentResult = valueProvider.GetValue("nonExistent"); ValueProviderResult removeMeResult = valueProvider.GetValue("removeme"); // Assert Assert.Null(nonExistentResult); Assert.Equal("removeMeValue", removeMeResult.AttemptedValue); Assert.Equal(CultureInfo.InvariantCulture, removeMeResult.Culture); // Verify that keys have been removed or retained correctly by the provider Mock mockTempDataProvider = new Mock(); string[] retainedKeys = null; mockTempDataProvider .Setup(o => o.SaveTempData(controllerContext, It.IsAny>())) .Callback( delegate(ControllerContext cc, IDictionary d) { retainedKeys = d.Keys.ToArray(); }); tempData.Save(controllerContext, mockTempDataProvider.Object); Assert.Equal(expectedRetainedKeys, retainedKeys); } [Fact] public void GetValueProvider_EmptyTempData_ReturnsNull() { // Arrange TempDataDictionary tempData = new TempDataDictionary(); ControllerContext controllerContext = GetControllerContext(tempData); TempDataValueProviderFactory factory = new TempDataValueProviderFactory(); // Act IValueProvider provider = factory.GetValueProvider(controllerContext); // Assert Assert.Null(provider); } private static ControllerContext GetControllerContext(TempDataDictionary tempData) { return new ControllerContext { Controller = new EmptyController { TempData = tempData } }; } private sealed class EmptyController : Controller { } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/TypeHelpersTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class TypeHelpersTest { [Fact] public void GetTypeArgumentsIfMatch_ClosedTypeIsGenericAndMatches_ReturnsType() { // Act Type[] typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(typeof(List), typeof(List<>)); // Assert Assert.Equal(new[] { typeof(int) }, typeArguments); } [Fact] public void GetTypeArgumentsIfMatch_ClosedTypeIsGenericButDoesNotMatch_ReturnsNull() { // Act Type[] typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(typeof(int?), typeof(List<>)); // Assert Assert.Null(typeArguments); } [Fact] public void GetTypeArgumentsIfMatch_ClosedTypeIsNotGeneric_ReturnsNull() { // Act Type[] typeArguments = TypeHelpers.GetTypeArgumentsIfMatch(typeof(int), null); // Assert Assert.Null(typeArguments); } [Fact] public void IsCompatibleObjectReturnsTrueIfTypeIsNotNullableAndValueIsNull() { // Act bool retVal = TypeHelpers.IsCompatibleObject(typeof(int), null); // Assert Assert.False(retVal); } [Fact] public void IsCompatibleObjectReturnsFalseIfValueIsIncorrectType() { // Arrange object value = new[] { "Hello", "world" }; // Act bool retVal = TypeHelpers.IsCompatibleObject(typeof(int), value); // Assert Assert.False(retVal); } [Fact] public void IsCompatibleObjectReturnsTrueIfTypeIsNullableAndValueIsNull() { // Act bool retVal = TypeHelpers.IsCompatibleObject(typeof(int?), null); // Assert Assert.True(retVal); } [Fact] public void IsCompatibleObjectReturnsTrueIfValueIsOfCorrectType() { // Arrange object value = new[] { "Hello", "world" }; // Act bool retVal = TypeHelpers.IsCompatibleObject(typeof(IEnumerable), value); // Assert Assert.True(retVal); } [Fact] public void TypeAllowsNullValueReturnsFalseForNonNullableGenericValueType() { Assert.False(TypeHelpers.TypeAllowsNullValue(typeof(KeyValuePair))); } [Fact] public void TypeAllowsNullValueReturnsFalseForNonNullableGenericValueTypeDefinition() { Assert.False(TypeHelpers.TypeAllowsNullValue(typeof(KeyValuePair<,>))); } [Fact] public void TypeAllowsNullValueReturnsFalseForNonNullableValueType() { Assert.False(TypeHelpers.TypeAllowsNullValue(typeof(int))); } [Fact] public void TypeAllowsNullValueReturnsTrueForInterfaceType() { Assert.True(TypeHelpers.TypeAllowsNullValue(typeof(IDisposable))); } [Fact] public void TypeAllowsNullValueReturnsTrueForNullableType() { Assert.True(TypeHelpers.TypeAllowsNullValue(typeof(int?))); } [Fact] public void TypeAllowsNullValueReturnsTrueForReferenceType() { Assert.True(TypeHelpers.TypeAllowsNullValue(typeof(object))); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/UrlAttributeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Web.Mvc; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.Mvc.Test { public class UrlAttributeTest { [Fact] public void ClientRule() { // Arrange var attribute = new UrlAttribute(); var provider = new Mock(); var metadata = new ModelMetadata(provider.Object, null, null, typeof(string), "PropertyName"); // Act ModelClientValidationRule clientRule = attribute.GetClientValidationRules(metadata, null).Single(); // Assert Assert.Equal("url", clientRule.ValidationType); Assert.Equal("The PropertyName field is not a valid fully-qualified http, https, or ftp URL.", clientRule.ErrorMessage); Assert.Empty(clientRule.ValidationParameters); } [Fact] public void IsValidTests() { // Arrange var attribute = new UrlAttribute(); // Act & Assert Assert.True(attribute.IsValid(null)); // Optional values are always valid Assert.True(attribute.IsValid("http://foo.bar")); Assert.True(attribute.IsValid("https://foo.bar")); Assert.True(attribute.IsValid("ftp://foo.bar")); Assert.False(attribute.IsValid("file:///foo.bar")); Assert.False(attribute.IsValid("http://user%password@foo.bar/")); Assert.False(attribute.IsValid("foo.png")); Assert.False(attribute.IsValid("\0foo.png")); // Illegal character } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/ValueProviderUtilTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace Microsoft.Web.Mvc.Test { public class ValueProviderUtilTest { [Fact] public void IsPrefixMatch_Misses() { // Arrange var tests = new[] { new { Prefix = "Prefix", TestString = (string)null, Reason = "Null test string shouldn't match anything." }, new { Prefix = "Foo", TestString = "NotFoo", Reason = "Prefix 'foo' doesn't match 'notfoo'." }, new { Prefix = "Foo", TestString = "FooBar", Reason = "Prefix 'foo' was not followed by a delimiter in the test string." } }; // Act & assert foreach (var test in tests) { bool retVal = ValueProviderUtil.IsPrefixMatch(test.Prefix, test.TestString); Assert.False(retVal, test.Reason); } } [Fact] public void IsPrefixMatch_Hits() { // Arrange var tests = new[] { new { Prefix = "", TestString = "SomeTestString", Reason = "Empty prefix should match any non-null test string." }, new { Prefix = "SomeString", TestString = "SomeString", Reason = "This was an exact match." }, new { Prefix = "Foo", TestString = "foo.bar", Reason = "Prefix 'foo' matched." }, new { Prefix = "Foo", TestString = "foo[bar]", Reason = "Prefix 'foo' matched." }, }; // Act & assert foreach (var test in tests) { bool retVal = ValueProviderUtil.IsPrefixMatch(test.Prefix, test.TestString); Assert.True(retVal, test.Reason); } } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/Test/VersionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Web.Mvc; using Microsoft.TestCommon; namespace Microsoft.Web.Test { public class VersionTest { [Fact] public void VerifyMVCVersionChangesAreIntentional() { Version mvcVersion = VersionTestHelper.GetVersionFromAssembly("System.Web.Mvc", typeof(Controller)); Assert.Equal(new Version(5, 3, 0, 0), mvcVersion); } } } ================================================ FILE: test/Microsoft.Web.Mvc.Test/packages.config ================================================  ================================================ FILE: test/Microsoft.Web.WebPages.OAuth.Test/Microsoft.Web.WebPages.OAuth.Test.csproj ================================================  {694C6EDF-EA52-438F-B745-82B025ECC0E7} Library Properties Microsoft.Web.WebPages.OAuth.Test Microsoft.Web.WebPages.OAuth.Test ..\..\bin\$(Configuration)\Test\ ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll True False ..\..\packages\DotNetOpenAuth.AspNet.4.0.3.12153\lib\net40-full\DotNetOpenAuth.AspNet.dll False ..\..\packages\DotNetOpenAuth.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.Core.dll False ..\..\packages\DotNetOpenAuth.OAuth.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OAuth.dll False ..\..\packages\DotNetOpenAuth.OAuth.Consumer.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OAuth.Consumer.dll False ..\..\packages\DotNetOpenAuth.OpenId.Core.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OpenId.dll False ..\..\packages\DotNetOpenAuth.OpenId.RelyingParty.4.0.3.12153\lib\net40-full\DotNetOpenAuth.OpenId.RelyingParty.dll ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True Code {4CBFC7D3-1600-4CE5-BC6B-AC7BC2D6F853} Microsoft.Web.WebPages.OAuth {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} Microsoft.TestCommon This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/Microsoft.Web.WebPages.OAuth.Test/OAuthWebSecurityTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Specialized; using System.Linq; using System.Web; using System.Web.Security; using DotNetOpenAuth.AspNet; using Microsoft.TestCommon; using Moq; namespace Microsoft.Web.WebPages.OAuth.Test { public class OAuthWebSecurityTest : IDisposable { [Fact] public void RegisterClientThrowsOnNullValue() { Assert.ThrowsArgumentNull(() => OAuthWebSecurity.RegisterClient(null), "client"); } [Fact] public void RegisterClientThrowsIfProviderNameIsEmpty() { // Arrange var client = new Mock(); client.Setup(c => c.ProviderName).Returns((string)null); // Act & Assert Assert.ThrowsArgument(() => OAuthWebSecurity.RegisterClient(client.Object), "client"); client.Setup(c => c.ProviderName).Returns(""); // Act & Assert Assert.ThrowsArgument(() => OAuthWebSecurity.RegisterClient(client.Object), "client"); } [Fact] public void RegisterClientThrowsRegisterMoreThanOneClientWithTheSameName() { // Arrange var client1 = new Mock(); client1.Setup(c => c.ProviderName).Returns("provider"); var client2 = new Mock(); client2.Setup(c => c.ProviderName).Returns("provider"); OAuthWebSecurity.RegisterClient(client1.Object); // Act & Assert Assert.ThrowsArgument(() => OAuthWebSecurity.RegisterClient(client2.Object), null); } [Fact] public void RequestAuthenticationRedirectsToProviderWithNullReturnUrl() { var cookies = new HttpCookieCollection(); // Arrange var context = new Mock(); context.Setup(c => c.Response.Cookies).Returns(cookies); context.Setup(c => c.Request.ServerVariables).Returns(new NameValueCollection()); context.Setup(c => c.Request.Url).Returns(new Uri("http://live.com/login.aspx")); context.Setup(c => c.Request.RawUrl).Returns("/login.aspx"); context.Setup(c => c.User.Identity.IsAuthenticated).Returns(false); var client = new Mock(); client.Setup(c => c.ProviderName).Returns("windowslive"); client.Setup(c => c.RequestAuthentication( context.Object, It.Is(u => u.AbsoluteUri.StartsWith("http://live.com/login.aspx?__provider__=windowslive", StringComparison.OrdinalIgnoreCase)))) .Verifiable(); OAuthWebSecurity.RegisterClient(client.Object); // Act OAuthWebSecurity.RequestAuthenticationCore(context.Object, "windowslive", null); // Assert client.Verify(); } [Fact] public void RequestAuthenticationRedirectsToProviderWithReturnUrl() { var cookies = new HttpCookieCollection(); // Arrange var context = new Mock(); context.Setup(c => c.Request.ServerVariables).Returns(new NameValueCollection()); context.Setup(c => c.Request.Url).Returns(new Uri("http://live.com/login.aspx")); context.Setup(c => c.Request.RawUrl).Returns("/login.aspx"); context.Setup(c => c.Response.Cookies).Returns(cookies); context.Setup(c => c.User.Identity.IsAuthenticated).Returns(false); var client = new Mock(); client.Setup(c => c.ProviderName).Returns("yahoo"); client.Setup(c => c.RequestAuthentication( context.Object, It.Is(u => u.AbsoluteUri.StartsWith("http://yahoo.com/?__provider__=yahoo", StringComparison.OrdinalIgnoreCase)))) .Verifiable(); OAuthWebSecurity.RegisterClient(client.Object); // Act OAuthWebSecurity.RequestAuthenticationCore(context.Object, "yahoo", "http://yahoo.com"); // Assert client.Verify(); } [Fact] public void VerifyAuthenticationFail() { // Arrange var queryStrings = new NameValueCollection(); queryStrings.Add("__provider__", "twitter"); var context = new Mock(); context.Setup(c => c.Request.QueryString).Returns(queryStrings); var client = new Mock(MockBehavior.Strict); client.Setup(c => c.ProviderName).Returns("facebook"); client.Setup(c => c.VerifyAuthentication(context.Object)).Returns(new AuthenticationResult(true, "facebook", "123", "super", null)); var anotherClient = new Mock(MockBehavior.Strict); anotherClient.Setup(c => c.ProviderName).Returns("twitter"); anotherClient.Setup(c => c.VerifyAuthentication(context.Object)).Returns(AuthenticationResult.Failed); OAuthWebSecurity.RegisterClient(client.Object); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act AuthenticationResult result = OAuthWebSecurity.VerifyAuthenticationCore(context.Object, "one.aspx"); // Assert Assert.False(result.IsSuccessful); Assert.Equal("twitter", result.Provider); } [Fact] public void VerifyAuthenticationFailIfNoProviderInQueryString() { // Arrange var context = new Mock(); context.Setup(c => c.Request.QueryString).Returns(new NameValueCollection()); var client = new Mock(MockBehavior.Strict); client.Setup(c => c.ProviderName).Returns("facebook"); var anotherClient = new Mock(MockBehavior.Strict); anotherClient.Setup(c => c.ProviderName).Returns("twitter"); OAuthWebSecurity.RegisterClient(client.Object); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act AuthenticationResult result = OAuthWebSecurity.VerifyAuthenticationCore(context.Object, ""); // Assert Assert.False(result.IsSuccessful); Assert.Null(result.Provider); } [Fact] public void LoginSetAuthenticationTicketIfSuccessful() { var originalProvider = OAuthWebSecurity.OAuthDataProvider; try { // Arrange var cookies = new HttpCookieCollection(); var context = new Mock(); context.Setup(c => c.Request.IsSecureConnection).Returns(true); context.Setup(c => c.Response.Cookies).Returns(cookies); var dataProvider = new Mock(MockBehavior.Strict); dataProvider.Setup(p => p.GetUserNameFromOpenAuth("twitter", "12345")).Returns("hola"); OAuthWebSecurity.OAuthDataProvider = dataProvider.Object; OAuthWebSecurity.RegisterTwitterClient("sdfdsfsd", "dfdsfdsf"); // Act bool successful = OAuthWebSecurity.LoginCore(context.Object, "twitter", "12345", createPersistentCookie: false); // Assert Assert.True(successful); Assert.Single(cookies); HttpCookie addedCookie = cookies[0]; Assert.Equal(FormsAuthentication.FormsCookieName, addedCookie.Name); Assert.True(addedCookie.HttpOnly); Assert.Equal("/", addedCookie.Path); Assert.False(addedCookie.Secure); Assert.False(String.IsNullOrEmpty(addedCookie.Value)); FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(addedCookie.Value); Assert.NotNull(ticket); Assert.Equal(2, ticket.Version); Assert.Equal("hola", ticket.Name); Assert.Equal("OAuth", ticket.UserData); Assert.False(ticket.IsPersistent); } finally { OAuthWebSecurity.OAuthDataProvider = originalProvider; } } [Fact] public void LoginFailIfUserIsNotFound() { var originalProvider = OAuthWebSecurity.OAuthDataProvider; try { // Arrange var context = new Mock(); OAuthWebSecurity.RegisterTwitterClient("consumerKey", "consumerSecrte"); var dataProvider = new Mock(); dataProvider.Setup(p => p.GetUserNameFromOpenAuth("twitter", "12345")).Returns((string)null); OAuthWebSecurity.OAuthDataProvider = dataProvider.Object; // Act bool successful = OAuthWebSecurity.LoginCore(context.Object, "twitter", "12345", createPersistentCookie: false); // Assert Assert.False(successful); } finally { OAuthWebSecurity.OAuthDataProvider = originalProvider; } } [Fact] public void GetOAuthClientReturnsTheCorrectClient() { // Arrange var client = new Mock(); client.Setup(c => c.ProviderName).Returns("facebook"); OAuthWebSecurity.RegisterClient(client.Object); var anotherClient = new Mock(); anotherClient.Setup(c => c.ProviderName).Returns("hulu"); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act var expectedClient = OAuthWebSecurity.GetOAuthClient("facebook"); // Assert Assert.Same(expectedClient, client.Object); } [Fact] public void GetOAuthClientThrowsIfClientIsNotFound() { // Arrange var client = new Mock(); client.Setup(c => c.ProviderName).Returns("facebook"); OAuthWebSecurity.RegisterClient(client.Object); var anotherClient = new Mock(); anotherClient.Setup(c => c.ProviderName).Returns("hulu"); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act & Assert Assert.Throws(() => OAuthWebSecurity.GetOAuthClient("live")); } [Fact] public void TryGetOAuthClientSucceeds() { // Arrange var client = new Mock(); client.Setup(c => c.ProviderName).Returns("facebook"); OAuthWebSecurity.RegisterClient(client.Object); var anotherClient = new Mock(); anotherClient.Setup(c => c.ProviderName).Returns("hulu"); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act IAuthenticationClient expectedClient; bool result = OAuthWebSecurity.TryGetOAuthClient("facebook", out expectedClient); // Assert Assert.Same(expectedClient, client.Object); Assert.True(result); } [Fact] public void TryGetOAuthClientFail() { // Arrange var client = new Mock(); client.Setup(c => c.ProviderName).Returns("facebook"); OAuthWebSecurity.RegisterClient(client.Object); var anotherClient = new Mock(); anotherClient.Setup(c => c.ProviderName).Returns("hulu"); OAuthWebSecurity.RegisterClient(anotherClient.Object); // Act IAuthenticationClient expectedClient; bool result = OAuthWebSecurity.TryGetOAuthClient("live", out expectedClient); // Assert Assert.Null(expectedClient); Assert.False(result); } [Fact] public void TestRegisterFacebookClient() { // Arrange OAuthWebSecurity.RegisterFacebookClient("one", "two", displayName: "FB", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("facebook", data.First().AuthenticationClient.ProviderName); Assert.Equal("FB", data.First().DisplayName); Assert.Null(data.First().ExtraData); } [Fact] public void TestRegisterMicrosoftClient() { // Arrange OAuthWebSecurity.RegisterMicrosoftClient("one", "two", displayName: "MS", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("microsoft", data.First().AuthenticationClient.ProviderName); Assert.Equal("MS", data.First().DisplayName); Assert.Null(data.First().ExtraData); } [Fact] public void TestRegisterTwitterClient() { // Arrange OAuthWebSecurity.RegisterTwitterClient("x0", "y0", displayName: "Tweet", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("twitter", data.First().AuthenticationClient.ProviderName); Assert.Equal("Tweet", data.First().DisplayName); Assert.Null(data.First().ExtraData); } [Fact] public void TestRegisterLinkedInClient() { // Arrange OAuthWebSecurity.RegisterLinkedInClient("x0", "y0", displayName: "LINKED", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("linkedIn", data.First().AuthenticationClient.ProviderName); Assert.Equal("LINKED", data.First().DisplayName); Assert.Null(data.First().ExtraData); } [Fact] public void TestRegisterGoogleClient() { // Arrange OAuthWebSecurity.RegisterGoogleClient(displayName: "GOOG", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("google", data.First().AuthenticationClient.ProviderName); Assert.Equal("GOOG", data.First().DisplayName); Assert.Null(data.First().ExtraData); } [Fact] public void TestRegisterYahooClient() { // Arrange OAuthWebSecurity.RegisterYahooClient(displayName: "YHOO", extraData: null); // Act var data = OAuthWebSecurity.RegisteredClientData; // Assert Assert.True(data.IsReadOnly); Assert.Equal(1, data.Count); Assert.Equal("yahoo", data.First().AuthenticationClient.ProviderName); Assert.Equal("YHOO", data.First().DisplayName); Assert.Null(data.First().ExtraData); } public void Dispose() { OAuthWebSecurity.ClearProviders(); } } } ================================================ FILE: test/Microsoft.Web.WebPages.OAuth.Test/PreAppStartCodeTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Web.WebPages.TestUtils; using Microsoft.TestCommon; namespace Microsoft.Web.WebPages.OAuth.Test { public class PreAppStartCodeTest { [Fact] public void TestPreAppStartClass() { PreAppStartTestHelper.TestPreAppStartClass(typeof(PreApplicationStartCode)); } } } ================================================ FILE: test/Microsoft.Web.WebPages.OAuth.Test/packages.config ================================================  ================================================ FILE: test/Settings.StyleCop ================================================ Parent False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False False ================================================ FILE: test/System.Net.Http.Formatting.Test/ByteRangeStreamContentTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class ByteRangeStreamContentTest { private MediaTypeHeaderValue _expectedMediatype = MediaTypeHeaderValue.Parse("text/test"); public static TheoryDataSet SingleRanges { get { // string ranges, int innerStreamLength, string expectedContentRange) return new TheoryDataSet { { "bytes=0-9", 10, "bytes 0-9/10" }, { "bytes=0-0", 10, "bytes 0-0/10" }, { "bytes=9-9", 10, "bytes 9-9/10" }, { "bytes=9-20", 10, "bytes 9-9/10" }, { "bytes=1-", 10, "bytes 1-9/10" }, { "bytes=8-", 10, "bytes 8-9/10" }, { "bytes=9-", 10, "bytes 9-9/10" }, { "bytes=-1", 10, "bytes 9-9/10" }, { "bytes=-2", 10, "bytes 8-9/10" }, { "bytes=-20", 10, "bytes 0-9/10" }, { "bytes=-9", 10, "bytes 1-9/10" }, { "bytes=-10", 10, "bytes 0-9/10" }, }; } } public static TheoryDataSet MultiRanges { get { // string ranges, int innerStreamLength, int expectedBodyparts, string[] contentRanges) return new TheoryDataSet { { "bytes=0-9,0-0", 10, 2, new string[] { "bytes 0-9/10", "bytes 0-0/10" } }, { "bytes=0-0,0-9", 10, 2, new string[] { "bytes 0-0/10", "bytes 0-9/10" } }, { "bytes=0-0,9-20", 10, 2, new string[] { "bytes 0-0/10", "bytes 9-9/10" } }, { "bytes=0-0,9-9,9-20", 10, 3, new string[] { "bytes 0-0/10", "bytes 9-9/10", "bytes 9-9/10" } }, { "bytes=0-0,9-9,10-20", 10, 2, new string[] { "bytes 0-0/10", "bytes 9-9/10" } }, }; } } public static TheoryDataSet NoOverlappingRanges { get { // string ranges, int innerStreamLength, string expectedContentRange) return new TheoryDataSet { { "bytes=100-", 10, "bytes */10" }, { "bytes=100-,200-,300-", 10, "bytes */10" }, }; } } [Fact] public void Ctor_ThrowsOnNullContent() { RangeHeaderValue range = new RangeHeaderValue(); Assert.ThrowsArgumentNull(() => new ByteRangeStreamContent( content: null, range: range, mediaType: _expectedMediatype, bufferSize: 128), "content"); } [Fact] public void Ctor_ThrowsIfCantSeekContent() { // Arrange Mock mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(false); RangeHeaderValue range = new RangeHeaderValue(); // Act/Assert Assert.ThrowsArgument(() => new ByteRangeStreamContent( content: mockInnerStream.Object, range: range, mediaType: _expectedMediatype, bufferSize: 128), "content"); } [Fact] public void Ctor_ThrowsOnNullRange() { Assert.ThrowsArgumentNull(() => new ByteRangeStreamContent( content: Stream.Null, range: null, mediaType: _expectedMediatype, bufferSize: 128), "range"); } [Fact] public void Ctor_ThrowsOnNullMediaType() { RangeHeaderValue range = new RangeHeaderValue(); Assert.ThrowsArgumentNull(() => new ByteRangeStreamContent( content: Stream.Null, range: range, mediaType: (MediaTypeHeaderValue)null, bufferSize: 128), "mediaType"); } [Fact] public void Ctor_ThrowsOnNullMediaTypeString() { RangeHeaderValue range = new RangeHeaderValue(); Assert.ThrowsArgument(() => new ByteRangeStreamContent( content: Stream.Null, range: range, mediaType: (String)null, bufferSize: 128), "mediaType", allowDerivedExceptions: true); } [Fact] public void Ctor_ThrowsOnInvalidBufferSize() { RangeHeaderValue range = new RangeHeaderValue(); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new ByteRangeStreamContent( content: Stream.Null, range: range, mediaType: _expectedMediatype, bufferSize: 0), "bufferSize", "1", "0"); } [Fact] public void Ctor_ThrowsOnNonByteRangeUnit() { RangeHeaderValue range = RangeHeaderValue.Parse("pages=0-9"); Assert.ThrowsArgument(() => new ByteRangeStreamContent( content: Stream.Null, range: range, mediaType: _expectedMediatype, bufferSize: 128), "range"); } [Fact] public void Ctor_ThrowsOnNoByteRanges() { RangeHeaderValue range = new RangeHeaderValue() { Unit = "bytes" }; Assert.ThrowsArgument(() => new ByteRangeStreamContent( content: Stream.Null, range: range, mediaType: _expectedMediatype, bufferSize: 128), "range"); } [Theory] [InlineData("bytes=-1")] [InlineData("bytes=0-")] [InlineData("bytes=0-,10-20,9-9")] public void RangesOverZeroLengthStream(string ranges) { // Arrange RangeHeaderValue range = RangeHeaderValue.Parse(ranges); // Act try { new ByteRangeStreamContent(Stream.Null, range, _expectedMediatype); } catch (InvalidByteRangeException invalidByteRangeException) { ContentRangeHeaderValue expectedContentRange = new ContentRangeHeaderValue(length: 0); Assert.Equal(expectedContentRange, invalidByteRangeException.ContentRange); } } [Theory] [PropertyData("SingleRanges")] public void SingleRangeGeneratesNonMultipartContent(string ranges, int innerStreamLength, string contentRange) { // Arrange string data = new String('a', innerStreamLength); byte[] bytes = Encoding.UTF8.GetBytes(data); MemoryStream memStream = new MemoryStream(bytes); RangeHeaderValue range = RangeHeaderValue.Parse(ranges); // Act ByteRangeStreamContent rangeContent = new ByteRangeStreamContent(memStream, range, _expectedMediatype); // Assert Assert.Equal(_expectedMediatype, rangeContent.Headers.ContentType); ContentRangeHeaderValue expectedContentRange = ContentRangeHeaderValue.Parse(contentRange); Assert.Equal(expectedContentRange, rangeContent.Headers.ContentRange); } [Theory] [PropertyData("MultiRanges")] public async Task MultipleRangesGeneratesMultipartByteRangesContent(string ranges, int innerStreamLength, int expectedBodyparts, string[] contentRanges) { // Arrange string data = new String('a', innerStreamLength); byte[] bytes = Encoding.UTF8.GetBytes(data); MemoryStream memStream = new MemoryStream(bytes); RangeHeaderValue range = RangeHeaderValue.Parse(ranges); // Act ByteRangeStreamContent content = new ByteRangeStreamContent(memStream, range, _expectedMediatype); MemoryStream result = new MemoryStream(); await content.CopyToAsync(result); MultipartMemoryStreamProvider multipart = await content.ReadAsMultipartAsync(); // Assert Assert.Equal(expectedBodyparts, multipart.Contents.Count); for (int count = 0; count < multipart.Contents.Count; count++) { MediaTypeHeaderValue contentType = multipart.Contents[count].Headers.ContentType; Assert.Equal(_expectedMediatype, contentType); ContentRangeHeaderValue expectedContentRange = ContentRangeHeaderValue.Parse(contentRanges[count]); ContentRangeHeaderValue contentRange = multipart.Contents[count].Headers.ContentRange; Assert.Equal(expectedContentRange, contentRange); } } [Theory] [PropertyData("NoOverlappingRanges")] public void NoOverlappingRangesThrowException(string ranges, int innerStreamLength, string contentRange) { // Arrange string data = new String('a', innerStreamLength); byte[] bytes = Encoding.UTF8.GetBytes(data); MemoryStream memStream = new MemoryStream(bytes); RangeHeaderValue range = RangeHeaderValue.Parse(ranges); // Act try { new ByteRangeStreamContent(memStream, range, _expectedMediatype); } catch (InvalidByteRangeException invalidByteRangeException) { ContentRangeHeaderValue expectedContentRange = ContentRangeHeaderValue.Parse(contentRange); Assert.Equal(expectedContentRange, invalidByteRangeException.ContentRange); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/CustomMultipartFormDataRemoteStreamProvider.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Net.Http.Headers; using Moq; namespace System.Net.Http { public class CustomMultipartFormDataRemoteStreamProvider : MultipartFormDataRemoteStreamProvider { public readonly string UrlBase = "http://some/path/to/"; public readonly List RemoteStreams = new List(); private readonly bool _isResultNull; public CustomMultipartFormDataRemoteStreamProvider() { } public CustomMultipartFormDataRemoteStreamProvider(bool isResultNull) { _isResultNull = isResultNull; } public override RemoteStreamInfo GetRemoteStream(HttpContent parent, HttpContentHeaders headers) { string fileName = headers.ContentDisposition.FileName; return _isResultNull ? null : new RemoteStreamInfo(CreateMockStream(), UrlBase + fileName, fileName); } private Stream CreateMockStream() { Stream stream = new Mock().Object; RemoteStreams.Add(stream); return stream; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/HttpTestData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets.Types; using System.Net.Http.Headers; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.DataSets { public class HttpTestData { public static TestData AllHttpMethods { get { return new RefTypeTestData(() => StandardHttpMethods.Concat(CustomHttpMethods).ToList()); } } public static TestData StandardHttpMethods { get { return new RefTypeTestData(() => new List() { HttpMethod.Head, HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete, HttpMethod.Options, HttpMethod.Trace, }); } } public static TestData CustomHttpMethods { get { return new RefTypeTestData(() => new List() { new HttpMethod("Custom") }); } } public static TestData AllHttpStatusCodes { get { return new ValueTypeTestData(new HttpStatusCode[] { HttpStatusCode.Accepted, HttpStatusCode.Ambiguous, HttpStatusCode.BadGateway, HttpStatusCode.BadRequest, HttpStatusCode.Conflict, HttpStatusCode.Continue, HttpStatusCode.Created, HttpStatusCode.ExpectationFailed, HttpStatusCode.Forbidden, HttpStatusCode.Found, HttpStatusCode.GatewayTimeout, HttpStatusCode.Gone, HttpStatusCode.HttpVersionNotSupported, HttpStatusCode.InternalServerError, HttpStatusCode.LengthRequired, HttpStatusCode.MethodNotAllowed, HttpStatusCode.Moved, HttpStatusCode.MovedPermanently, HttpStatusCode.MultipleChoices, HttpStatusCode.NoContent, HttpStatusCode.NonAuthoritativeInformation, HttpStatusCode.NotAcceptable, HttpStatusCode.NotFound, HttpStatusCode.NotImplemented, HttpStatusCode.NotModified, HttpStatusCode.OK, HttpStatusCode.PartialContent, HttpStatusCode.PaymentRequired, HttpStatusCode.PreconditionFailed, HttpStatusCode.ProxyAuthenticationRequired, HttpStatusCode.Redirect, HttpStatusCode.RedirectKeepVerb, HttpStatusCode.RedirectMethod, HttpStatusCode.RequestedRangeNotSatisfiable, HttpStatusCode.RequestEntityTooLarge, HttpStatusCode.RequestTimeout, HttpStatusCode.RequestUriTooLong, HttpStatusCode.ResetContent, HttpStatusCode.SeeOther, HttpStatusCode.ServiceUnavailable, HttpStatusCode.SwitchingProtocols, HttpStatusCode.TemporaryRedirect, HttpStatusCode.Unauthorized, HttpStatusCode.UnsupportedMediaType, HttpStatusCode.Unused, HttpStatusCode.UseProxy }); } } public static TestData CustomHttpStatusCodes { get { return new ValueTypeTestData(new HttpStatusCode[] { (HttpStatusCode)199, (HttpStatusCode)299, (HttpStatusCode)399, (HttpStatusCode)499, (HttpStatusCode)599, (HttpStatusCode)699, (HttpStatusCode)799, (HttpStatusCode)899, (HttpStatusCode)999, }); } } public static ReadOnlyCollection ConvertablePrimitiveValueTypes { get { return new ReadOnlyCollection(new TestData[] { TestData.CharTestData, TestData.IntTestData, TestData.UintTestData, TestData.ShortTestData, TestData.UshortTestData, TestData.LongTestData, TestData.UlongTestData, TestData.ByteTestData, TestData.SByteTestData, TestData.BoolTestData, TestData.DoubleTestData, TestData.FloatTestData, TestData.DecimalTestData, TestData.TimeSpanTestData, TestData.GuidTestData, TestData.DateTimeTestData, TestData.DateTimeOffsetTestData }); } } public static ReadOnlyCollection ConvertableEnumTypes { get { return new ReadOnlyCollection(new TestData[] { TestData.SimpleEnumTestData, TestData.LongEnumTestData, TestData.FlagsEnumTestData, DataContractEnumTestData }); } } public static ReadOnlyCollection ConvertableValueTypes { get { return new ReadOnlyCollection( ConvertablePrimitiveValueTypes.Concat(ConvertableEnumTypes).ToList()); } } public static TestData StandardBsonMediaTypes { get { return new RefTypeTestData(() => new List() { new MediaTypeHeaderValue("application/bson"), }); } } public static TestData StandardJsonMediaTypes { get { return new RefTypeTestData(() => new List() { new MediaTypeHeaderValue("application/json"), new MediaTypeHeaderValue("text/json") }); } } public static TestData StandardXmlMediaTypes { get { return new RefTypeTestData(() => new List() { new MediaTypeHeaderValue("application/xml"), new MediaTypeHeaderValue("text/xml") }); } } public static TestData StandardODataMediaTypes { get { return new RefTypeTestData(() => new List() { new MediaTypeHeaderValue("application/atom+xml"), new MediaTypeHeaderValue("application/json"), }); } } public static TestData StandardFormUrlEncodedMediaTypes { get { return new RefTypeTestData(() => new List() { new MediaTypeHeaderValue("application/x-www-form-urlencoded") }); } } public static TestData StandardJsonMediaTypeStrings { get { return new RefTypeTestData(() => new List() { "application/json", "text/json" }); } } public static TestData StandardXmlMediaTypeStrings { get { return new RefTypeTestData(() => new List() { "application/xml", "text/xml" }); } } public static TestData LegalMediaTypeStrings { get { return new RefTypeTestData(() => StandardXmlMediaTypeStrings.Concat(StandardJsonMediaTypeStrings).ToList()); } } // Illegal media type strings. These will cause the MediaTypeHeaderValue ctor to throw FormatException public static TestData IllegalMediaTypeStrings { get { return new RefTypeTestData(() => new List() { "\0", "9\r\n" }); } } public static TestData StandardEncodings { get { return new RefTypeTestData(() => new List() { new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true), }); } } public static TheoryDataSet ReadAndWriteCorrectCharacterEncoding { get { return new TheoryDataSet { { "This is a test 激光這兩個字是甚麼意思 string written using utf-8", "utf-8", true }, { "This is a test 激光這兩個字是甚麼意思 string written using utf-16", "utf-16", true }, { "This is a test 激光這兩個字是甚麼意思 string written using utf-32", "utf-32", false }, #if !NETCOREAPP // shift_jis and iso-2022-kr are not supported when running on .NET Core 2.1. { "This is a test 激光這兩個字是甚麼意思 string written using shift_jis", "shift_jis", false }, #endif { "This is a test æøå string written using iso-8859-1", "iso-8859-1", false }, #if !NETCOREAPP { "This is a test 레이저 단어 뜻 string written using iso-2022-kr", "iso-2022-kr", false }, #endif }; } } //// TODO: complete this list // Legal MediaTypeHeaderValues public static TestData LegalMediaTypeHeaderValues { get { return new RefTypeTestData( () => LegalMediaTypeStrings.Select(mediaType => new MediaTypeHeaderValue(mediaType)).ToList()); } } public static TestData StandardMediaTypesWithQuality { get { return new RefTypeTestData(() => new List() { new MediaTypeWithQualityHeaderValue("application/json", .1) { CharSet="utf-8"}, new MediaTypeWithQualityHeaderValue("text/json", .2) { CharSet="utf-8"}, new MediaTypeWithQualityHeaderValue("application/xml", .3) { CharSet="utf-8"}, new MediaTypeWithQualityHeaderValue("text/xml", .4) { CharSet="utf-8"}, new MediaTypeWithQualityHeaderValue("application/atom+xml", .5) { CharSet="utf-8"}, }); } } public static TestData StandardHttpContents { get { return new RefTypeTestData(() => new List() { new ByteArrayContent(new byte[0]), new FormUrlEncodedContent(new KeyValuePair[0]), new MultipartContent(), new StringContent(""), new StreamContent(new MemoryStream()) }); } } //// TODO: make this list compose from other data? // Collection of legal instances of all standard MediaTypeMapping types public static TestData StandardMediaTypeMappings { get { return new RefTypeTestData(() => QueryStringMappings.Cast().ToList()); } } public static TestData QueryStringMappings { get { return new RefTypeTestData(() => new List() { new QueryStringMapping("format", "json", new MediaTypeHeaderValue("application/json")) }); } } public static TestData LegalUriPathExtensions { get { return new RefTypeTestData(() => new List() { "xml", "json" }); } } public static TestData LegalQueryStringParameterNames { get { return new RefTypeTestData(() => new List() { "format", "fmt" }); } } public static TestData LegalHttpHeaderNames { get { return new RefTypeTestData(() => new List() { "x-requested-with", "some-random-name" }); } } public static TestData LegalHttpHeaderValues { get { return new RefTypeTestData(() => new List() { "1", "XMLHttpRequest", "\"quoted-string\"" }); } } public static TestData LegalQueryStringParameterValues { get { return new RefTypeTestData(() => new List() { "xml", "json" }); } } public static TestData LegalMediaRangeStrings { get { return new RefTypeTestData(() => new List() { "application/*", "text/*" }); } } public static TestData LegalMediaRangeValues { get { return new RefTypeTestData(() => LegalMediaRangeStrings.Select(s => new MediaTypeHeaderValue(s)).ToList() ); } } public static TestData MediaRangeValuesWithQuality { get { return new RefTypeTestData(() => new List() { new MediaTypeWithQualityHeaderValue("application/*", .1), new MediaTypeWithQualityHeaderValue("text/*", .2), }); } } public static TestData IllegalMediaRangeStrings { get { return new RefTypeTestData(() => new List() { "application/xml", "text/xml" }); } } public static TestData IllegalMediaRangeValues { get { return new RefTypeTestData(() => IllegalMediaRangeStrings.Select(s => new MediaTypeHeaderValue(s)).ToList() ); } } public static TestData StandardFormatters { get { return new RefTypeTestData(() => new List() { new XmlMediaTypeFormatter(), new JsonMediaTypeFormatter(), new FormUrlEncodedMediaTypeFormatter() }); } } public static TestData StandardFormatterTypes { get { return new RefTypeTestData(() => StandardFormatters.Select(m => m.GetType())); } } public static TestData DerivedFormatters { get { return new RefTypeTestData(() => new List() { new DerivedXmlMediaTypeFormatter(), new DerivedJsonMediaTypeFormatter(), new DerivedFormUrlEncodedMediaTypeFormatter(), }); } } public static TestData> AllFormatterCollections { get { return new RefTypeTestData>(() => new List>() { new MediaTypeFormatter[0], StandardFormatters, DerivedFormatters, }); } } public static TestData LegalHttpAddresses { get { return new RefTypeTestData(() => new List() { "http://somehost", "https://somehost", }); } } public static TestData AddressesWithIllegalSchemes { get { return new RefTypeTestData(() => new List() { "net.tcp://somehost", "file://somehost", "net.pipe://somehost", "mailto:somehost", "ftp://somehost", "news://somehost", "ws://somehost", "abc://somehost" }); } } /// /// A read-only collection of representative values and reference type test data. /// Uses where exhaustive coverage is not required. It includes null values. /// public static ReadOnlyCollection RepresentativeValueAndRefTypeTestDataCollection { get { return new ReadOnlyCollection(new TestData[] { TestData.ByteTestData, TestData.IntTestData, TestData.BoolTestData, TestData.SimpleEnumTestData, TestData.StringTestData, TestData.DateTimeTestData, TestData.DateTimeOffsetTestData, TestData.TimeSpanTestData, WcfPocoTypeTestDataWithNull }); } } public static TestData NullContentHttpRequestMessages { get { return new RefTypeTestData(() => new List() { new HttpRequestMessage() { Content = null }, }); } } public static TestData LegalHttpParameterNames { get { return new RefTypeTestData(() => new List() { "文", "A", "a", "b", " a", "arg1", "arg2", "1", "@", "!" }); } } public static TestData LegalHttpParameterTypes { get { return new RefTypeTestData(() => new List() { typeof(string), typeof(byte[]), typeof(byte[][]), typeof(char), typeof(DateTime), typeof(decimal), typeof(double), typeof(Guid), typeof(Int16), typeof(Int32), typeof(object), typeof(sbyte), typeof(Single), typeof(TimeSpan), typeof(UInt16), typeof(UInt32), typeof(UInt64), typeof(Uri), typeof(Enum), typeof(Collection), typeof(IList), typeof(System.Runtime.Serialization.ISerializable), typeof(System.Data.DataSet), typeof(System.Xml.Serialization.IXmlSerializable), typeof(Nullable), typeof(Nullable), typeof(Stream), typeof(HttpRequestMessage), typeof(HttpResponseMessage), typeof(ObjectContent), typeof(ObjectContent), typeof(HttpContent), typeof(Delegate), typeof(Action), typeof(System.Threading.Tasks.Task), typeof(System.Threading.Tasks.Task), typeof(List) }); } } /// /// Common for an enum decorated with a . /// public static ValueTypeTestData DataContractEnumTestData { get { return new ValueTypeTestData( DataContractEnum.First, DataContractEnum.Second); } } /// /// Common for the string form of a . /// public static RefTypeTestData UriTestDataStrings { get { return new RefTypeTestData(() => new List() { "http://somehost", "http://somehost:8080", "http://somehost/", "http://somehost:8080/", "http://somehost/somepath", "http://somehost/somepath/", "http://somehost/somepath?somequery=somevalue" }); } } /// /// Common for a . /// public static RefTypeTestData UriTestData { get { return new RefTypeTestData(() => UriTestDataStrings.Select(s => new Uri(s)).ToList()); } } /// /// Common for a POCO class type that includes null values /// for both the base class and derived classes. /// public static RefTypeTestData WcfPocoTypeTestDataWithNull { get { return new RefTypeTestData( WcfPocoType.GetTestDataWithNull, WcfPocoType.GetDerivedTypeTestDataWithNull, null); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DataContractEnum.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.Serialization; namespace System.Net.Http.Formatting.DataSets.Types { [DataContract] public enum DataContractEnum { [EnumMember] First, [EnumMember] Second, Third } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DataContractType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Runtime.Serialization; using System.Xml.Serialization; using Microsoft.TestCommon.Types; namespace System.Net.Http.Formatting.DataSets.Types { [DataContract] [KnownType(typeof(DerivedDataContractType))] [XmlInclude(typeof(DerivedDataContractType))] public class DataContractType : INameAndIdContainer { private int id; private string name; public DataContractType() { } public DataContractType(int id, string name) { this.id = id; this.name = name; } [DataMember] public int Id { get { return this.id; } set { this.IdSet = true; this.id = value; } } [DataMember] public string Name { get { return this.name; } set { this.NameSet = true; this.name = value; } } [XmlIgnore] public bool IdSet { get; private set; } [XmlIgnore] public bool NameSet { get; private set; } public static IEnumerable GetTestData() { return new DataContractType[] { new DataContractType(), new DataContractType(1, "SomeName") }; } public static IEnumerable GetDerivedTypeTestData() { return new DerivedDataContractType[] { new DerivedDataContractType(), new DerivedDataContractType(1, "SomeName", null), new DerivedDataContractType(1, "SomeName", new WcfPocoType(2, "SomeOtherName"))}; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedDataContractType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Runtime.Serialization; using System.Xml.Serialization; namespace System.Net.Http.Formatting.DataSets.Types { [DataContract] [KnownType(typeof(DerivedWcfPocoType))] [KnownType(typeof(DerivedDataContractType))] [XmlInclude(typeof(DerivedWcfPocoType))] [XmlInclude(typeof(DerivedDataContractType))] public class DerivedDataContractType : DataContractType { private WcfPocoType reference; public DerivedDataContractType() { } public DerivedDataContractType(int id, string name, WcfPocoType reference) : base(id, name) { this.reference = reference; } [DataMember] public WcfPocoType Reference { get { return this.reference; } set { this.ReferenceSet = true; this.reference = value; } } [XmlIgnore] public bool ReferenceSet { get; private set; } public static new IEnumerable GetTestData() { return new DerivedDataContractType[] { new DerivedDataContractType(), new DerivedDataContractType(1, "SomeName", new WcfPocoType(2, "SomeOtherName")) }; } public static IEnumerable GetKnownTypeTestData() { return new DerivedDataContractType[] { new DerivedDataContractType(), new DerivedDataContractType(1, "SomeName", null), new DerivedDataContractType(1, "SomeName", new DerivedWcfPocoType(2, "SomeOtherName", null))}; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedFormUrlEncodedMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.DataSets.Types { public class DerivedFormUrlEncodedMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter { } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedJsonMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.DataSets.Types { public class DerivedJsonMediaTypeFormatter : JsonMediaTypeFormatter { } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedWcfPocoType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.Serialization; using System.Xml.Serialization; namespace System.Net.Http.Formatting.DataSets.Types { public class DerivedWcfPocoType : WcfPocoType { private WcfPocoType reference; public DerivedWcfPocoType() { } public DerivedWcfPocoType(int id, string name, WcfPocoType reference) : base(id, name) { this.reference = reference; } public WcfPocoType Reference { get { return this.reference; } set { this.ReferenceSet = true; this.reference = value; } } [IgnoreDataMember] [XmlIgnore] public bool ReferenceSet { get; private set; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedXmlMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.DataSets.Types { public class DerivedXmlMediaTypeFormatter : XmlMediaTypeFormatter { } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/DerivedXmlSerializableType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Runtime.Serialization; using System.Xml.Serialization; namespace System.Net.Http.Formatting.DataSets.Types { [KnownType(typeof(DerivedWcfPocoType))] [XmlInclude(typeof(DerivedWcfPocoType))] public class DerivedXmlSerializableType : XmlSerializableType, INotJsonSerializable { private WcfPocoType reference; public DerivedXmlSerializableType() { } public DerivedXmlSerializableType(int id, string name, WcfPocoType reference) : base(id, name) { this.reference = reference; } [XmlElement] public WcfPocoType Reference { get { return this.reference; } set { this.ReferenceSet = true; this.reference = value; } } [XmlIgnore] public bool ReferenceSet { get; private set; } public static new IEnumerable GetTestData() { return new DerivedXmlSerializableType[] { new DerivedXmlSerializableType(), new DerivedXmlSerializableType(1, "SomeName", new WcfPocoType(2, "SomeOtherName")) }; } public static IEnumerable GetKnownTypeTestData() { return new DerivedXmlSerializableType[] { new DerivedXmlSerializableType(), new DerivedXmlSerializableType(1, "SomeName", null), new DerivedXmlSerializableType(1, "SomeName", new DerivedWcfPocoType(2, "SomeOtherName", null))}; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/INotJsonSerializable.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.DataSets.Types { /// /// Tagging interface to indicate types which we know Json cannot serialize. /// public interface INotJsonSerializable { } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/WcfPocoType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Xml.Serialization; using Microsoft.TestCommon.Types; namespace System.Net.Http.Formatting.DataSets.Types { [KnownType(typeof(DerivedWcfPocoType))] [XmlInclude(typeof(DerivedWcfPocoType))] public class WcfPocoType : INameAndIdContainer { private int id; private string name; public WcfPocoType() { } public WcfPocoType(int id, string name) { this.id = id; this.name = name; } public int Id { get { return this.id; } set { this.IdSet = true; this.id = value; } } public string Name { get { return this.name; } set { this.NameSet = true; this.name = value; } } [IgnoreDataMember] [XmlIgnore] public bool IdSet { get; private set; } [IgnoreDataMember] [XmlIgnore] public bool NameSet { get; private set; } public static IEnumerable GetTestData() { return new WcfPocoType[] { new WcfPocoType(), new WcfPocoType(1, "SomeName") }; } public static IEnumerable GetTestDataWithNull() { return GetTestData().Concat(new WcfPocoType[] { null }); } public static IEnumerable GetDerivedTypeTestData() { return new DerivedWcfPocoType[] { new DerivedWcfPocoType(), new DerivedWcfPocoType(1, "SomeName", null), new DerivedWcfPocoType(1, "SomeName", new WcfPocoType(2, "SomeOtherName"))}; } public static IEnumerable GetDerivedTypeTestDataWithNull() { return GetDerivedTypeTestData().Concat(new DerivedWcfPocoType[] { null }); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/DataSets/Types/XmlSerializableType.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Runtime.Serialization; using System.Xml.Serialization; using Microsoft.TestCommon.Types; namespace System.Net.Http.Formatting.DataSets.Types { [KnownType(typeof(DerivedXmlSerializableType))] [XmlInclude(typeof(DerivedXmlSerializableType))] public class XmlSerializableType : INameAndIdContainer { private int id; private string name; public XmlSerializableType() { } public XmlSerializableType(int id, string name) { this.id = id; this.name = name; } [XmlAttribute] public int Id { get { return this.id; } set { this.IdSet = true; this.id = value; } } [XmlElement] public string Name { get { return this.name; } set { this.NameSet = true; this.name = value; } } [XmlIgnore] public bool IdSet { get; private set; } [XmlIgnore] public bool NameSet { get; private set; } public override bool Equals(object obj) { if (Object.ReferenceEquals(this, obj)) { return true; } XmlSerializableType other = obj as XmlSerializableType; if (Object.ReferenceEquals(other, null)) { return false; } return String.Equals(this.Name, other.Name, StringComparison.Ordinal) && this.Id == other.Id; } public override int GetHashCode() { return base.GetHashCode(); } public static IEnumerable GetTestData() { return new XmlSerializableType[] { new XmlSerializableType(), new XmlSerializableType(1, "SomeName") }; } public static IEnumerable GetDerivedTypeTestData() { return new DerivedXmlSerializableType[] { new DerivedXmlSerializableType(), new DerivedXmlSerializableType(1, "SomeName", null), new DerivedXmlSerializableType(1, "SomeName", new WcfPocoType(2, "SomeOtherName"))}; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/BsonMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class BsonMediaTypeFormatterTests : MediaTypeFormatterTestBase { // Exclude IEnumerable and IQueryable to avoid attempts to round trip values that are known to cause // trouble in deserialization e.g. base IEnumerable. BSON reader won't know how to construct such types. private const TestDataVariations RoundTripVariations = (TestDataVariations.All | TestDataVariations.WithNull | TestDataVariations.AsClassMember) & ~(TestDataVariations.AsIEnumerable | TestDataVariations.AsIQueryable); /// /// Provide test data for round trip tests. Avoid types BSON does not support. /// /// BSON does not support some unsigned integers as well as having issues with . /// /// /// BSON writer attempts to write an unsigned int or long as a signed integer of the same size e.g. it writes /// an as an and thus can only write values less than /// Int32.MaxValue. BSON writer fortunately uses an for , /// , , and values. /// /// /// BSON successfully writes all values as . But BSON reader may not /// be able to be convert the value back e.g. Decimal.MaxValue loses precision when /// written and is rounded up -- to an invalid value. /// /// /// BSON (as well as JSON and default ToString() in the case) loses information /// when writing and values. BSON writer uses a UTC /// datetime value in both cases -- losing Kind and Offset property values, respectively. /// ( values are not currently included in /// but exclude /// to be safe.) /// /// /// BSON readers and writers appear to round trip values successfully. However /// does not implement or /// and thus fails. /// /// /// /// public static IEnumerable ValueAndRefTypeTestDataCollection { get { return CommonUnitTestDataSets.ValueAndRefTypeTestDataCollection.Except( new TestData[] { CommonUnitTestDataSets.Uints, CommonUnitTestDataSets.Ulongs, CommonUnitTestDataSets.DateTimeOffsets, CommonUnitTestDataSets.DateTimes, CommonUnitTestDataSets.Decimals, CommonUnitTestDataSets.ISerializableTypes, }); } } public override IEnumerable ExpectedSupportedMediaTypes { get { return HttpTestData.StandardBsonMediaTypes; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return new byte[17] { // Little-endian length 17, 0, 0, 0, // Opcode indicating a 32bit integer 0x10, // Field name as a C string (byte)'N', (byte)'u', (byte)'m', (byte)'b', (byte)'e', (byte)'r', 0, // Little-endian value 42, 0, 0, 0, // BSON document terminator 0, }; } } public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); string mediaType = string.Format("application/bson; charset={0}", encoding); Encoding enc = CreateOrGetSupportedEncoding(formatter, encoding, isDefaultEncoding); // Test roundtrip in this case, not against expectations byte[] sourceData; using (MemoryStream stream = new MemoryStream()) { formatter.WriteToStream(typeof(string), content, stream, enc); sourceData = stream.ToArray(); } // Further Arrange, Act & Assert return ReadContentUsingCorrectCharacterEncodingHelperAsync(formatter, content, sourceData, mediaType); } public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange GC.KeepAlive(isDefaultEncoding); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); string mediaType = string.Format("application/bson; charset={0}", encoding); Encoding enc = Encoding.GetEncoding(encoding); // Test sync and async approaches match, not against expectations // See ReadFromStreamAsync_UsesCorrectCharacterEncoding for roundtrip test byte[] expectedData; using (MemoryStream stream = new MemoryStream()) { formatter.WriteToStream(typeof(string), content, stream, enc); expectedData = stream.ToArray(); } // Further Arrange, Act & Assert return WriteContentUsingCorrectCharacterEncodingHelperAsync(formatter, content, expectedData, mediaType); } [Fact] void CopyConstructor() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter() { MaxDepth = 42, }; // Replace serializable settings and switch one property's value JsonSerializerSettings oldSettings = formatter.SerializerSettings; formatter.SerializerSettings = formatter.CreateDefaultSerializerSettings(); formatter.SerializerSettings.CheckAdditionalContent = !formatter.SerializerSettings.CheckAdditionalContent; // Act TestBsonMediaTypeFormatter derivedFormatter = new TestBsonMediaTypeFormatter(formatter); // Assert Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth); Assert.NotSame(oldSettings, formatter.SerializerSettings); Assert.NotEqual(oldSettings.CheckAdditionalContent, formatter.SerializerSettings.CheckAdditionalContent); Assert.Same(formatter.SerializerSettings, derivedFormatter.SerializerSettings); Assert.Same(formatter.SerializerSettings.ContractResolver, derivedFormatter.SerializerSettings.ContractResolver); } [Fact] public void MaxDepth_RoundTrips() { // Arrange & Act & Assert Assert.Reflection.IntegerProperty( new BsonMediaTypeFormatter(), c => c.MaxDepth, expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 256); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanReadType_ReturnsExpectedValues(Type variationType, object testData) { // Arrange GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); // Act & Assert Assert.True(formatter.CanReadType(variationType)); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanWriteType_ReturnsExpectedValues(Type variationType, object testData) { // Arrange GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); // Act & Assert Assert.True(formatter.CanWriteType(variationType)); } [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateFails() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter { ThrowExceptionOnCreate = true, }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "Throwing exception directly, since it needs to get caught by a catch all"); Assert.False(formatter.WasCreateJsonReaderCalled); Assert.True(formatter.WasCreateJsonWriterCalled); } [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateReturnsNull() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter { ReturnNullOncreate = true, }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "The 'CreateJsonWriter' method returned null. It must return a JSON writer instance."); Assert.False(formatter.WasCreateJsonReaderCalled); Assert.True(formatter.WasCreateJsonWriterCalled); } [Fact] public async Task FormatterThrowsOnWriteWithInvalidContent() { // Arrange (set up to serialize Int32.MaxValue + 1 as an UInt32; can't be done since serialization uses an Int32 Type variationType = typeof(uint); uint testData = (uint)Int32.MaxValue + 1u; BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); HttpContent content = new StringContent(String.Empty); MemoryStream stream = new MemoryStream(); string expectedMessage = "Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values. Path 'Value'."; // Act & Assert // Note error message is not quite correct: BSON supports byte, ushort, and smaller uint / ulong values. await Assert.ThrowsAsync( () => formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null), expectedMessage); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateFails() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter { ThrowExceptionOnCreate = true, }; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "Throwing exception directly, since it needs to get caught by a catch all"); Assert.True(formatter.WasCreateJsonReaderCalled); Assert.False(formatter.WasCreateJsonWriterCalled); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateReturnsNull() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter { ReturnNullOncreate = true, }; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "The 'CreateJsonReader' method returned null. It must return a JSON reader instance."); Assert.True(formatter.WasCreateJsonReaderCalled); Assert.False(formatter.WasCreateJsonWriterCalled); } [Fact] public async Task FormatterThrowsOnReadWithInvalidContent() { // Arrange (serialize Decimal.MaxValue; can't be read back since serialization uses rounded Double) Type variationType = typeof(decimal); decimal testData = Decimal.MaxValue; BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; MemoryStream stream = new MemoryStream(); await formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null); contentHeaders.ContentLength = stream.Length; stream.Flush(); stream.Seek(0L, SeekOrigin.Begin); // Act & Assert await Assert.ThrowsAsync( () => formatter.ReadFromStreamAsync(variationType, stream, content, null), #if NETCOREAPP3_1_OR_GREATER "Could not convert to decimal: 7.922816251426434E+28. Path 'Value'." #else "Could not convert to decimal: 7.92281625142643E+28. Path 'Value'." #endif ); } [Theory] [InlineData(typeof(IList))] [InlineData(typeof(IDictionary))] public async Task UseBsonFormatterWithNullCollections(Type type) { // Arrange BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(type, null, memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.Empty(serializedString); } [Theory] [TestDataSet(typeof(BsonMediaTypeFormatterTests), "ValueAndRefTypeTestDataCollection", RoundTripVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync(Type variationType, object testData) { // Arrange BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "BunchOfJsonObjectsTestDataCollection", RoundTripVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_PerhapsJObject(Type variationType, object testData) { // Arrange BsonMediaTypeFormatter formatter = new BsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); JObject readJObject = readObj as JObject; if (readJObject != null) { // Serialized a Dictionary to handle simple runtime type; round trips as a JObject Assert.Single(readJObject); JToken readJToken = readJObject["Value"]; Assert.NotNull(readJToken); Assert.Equal(testData, readJToken.ToObject(testData.GetType())); } else { Assert.Equal(testData, readObj); } } // Test alternate null value [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AllSingleInstances)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull(Type variationType, object testData) { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Assert.Null(readObj); } #if !NETCOREAPP2_1 // DBNull not serializable on .NET Core 2.1 except at top level (using BsonMediaTypeformatter special case). [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsDictionary)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Dictionary(Type variationType, object testData) { // Guard IDictionary expectedDictionary = Assert.IsType>(testData); // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into collections. Assert.Equal(testData.GetType(), readObj.GetType()); IDictionary readDictionary = (IDictionary)readObj; Assert.Equal(expectedDictionary.Count, readDictionary.Count); foreach (string key in expectedDictionary.Keys) { Assert.True(readDictionary.ContainsKey(key)); Assert.Null(readDictionary[key]); } } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsArray | TestDataVariations.AsList)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Enumerable(Type variationType, object testData) { // Guard Assert.True((testData as IEnumerable) != null); // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); IEnumerable expectedEnumerable = (IEnumerable)testData; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into collections. Assert.Equal(testData.GetType(), readObj.GetType()); IEnumerable readEnumerable = (IEnumerable)readObj; Assert.Equal(expectedEnumerable.Count(), readEnumerable.Count()); foreach (object readContent in readEnumerable) { Assert.Null(readContent); } } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsClassMember)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Holder(Type variationType, object testData) { // Guard Assert.IsType>(testData); // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into objects. Assert.Equal(testData.GetType(), readObj.GetType()); TestDataHolder readDataHolder = (TestDataHolder)readObj; Assert.Null(readDataHolder.V1); } #endif [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNullString() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); Type variationType = typeof(string); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // Null on wire can be read as null of any nullable type Assert.Null(readObj); } [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNull() { // Arrange TestBsonMediaTypeFormatter formatter = new TestBsonMediaTypeFormatter(); Type variationType = typeof(DBNull); object testData = DBNull.Value; // Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // Only BSON case where DBNull.Value round-trips Assert.Equal(testData, readObj); } private class TestBsonMediaTypeFormatter : BsonMediaTypeFormatter { public TestBsonMediaTypeFormatter() : base() { } public TestBsonMediaTypeFormatter(TestBsonMediaTypeFormatter formatter) : base(formatter) { } public bool ReturnNullOncreate { get; set; } public bool ThrowExceptionOnCreate { get; set; } public bool WasCreateJsonReaderCalled { get; private set; } public bool WasCreateJsonWriterCalled { get; private set; } public override JsonReader CreateJsonReader(Type type, Stream readStream, Encoding effectiveEncoding) { WasCreateJsonReaderCalled = true; if (ReturnNullOncreate) { return null; } if (ThrowExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return base.CreateJsonReader(type, readStream, effectiveEncoding); } public override JsonWriter CreateJsonWriter(Type type, Stream writeStream, Encoding effectiveEncoding) { WasCreateJsonWriterCalled = true; if (ReturnNullOncreate) { return null; } if (ThrowExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return base.CreateJsonWriter(type, writeStream, effectiveEncoding); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/BufferedMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class BufferedMediaTypeFormatterTests : MediaTypeFormatterTestBase { private const string ExpectedSupportedMediaType = "text/test"; private const string TestData = "Hello World Hello World Hello World Hello World Hello World Hello World"; public override IEnumerable ExpectedSupportedMediaTypes { get { return new List { new MediaTypeHeaderValue(ExpectedSupportedMediaType) }; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return ExpectedSupportedEncodings.ElementAt(0).GetBytes("System.Net.Http.Formatting.MediaTypeFormatterTestBase`1+SampleType[System.Net.Http.Formatting.MockBufferedMediaTypeFormatter]"); } } [Fact] void CopyConstructor() { MockBufferedMediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter() { BufferSize = 512 }; MockBufferedMediaTypeFormatter derivedFormatter = new MockBufferedMediaTypeFormatter(formatter); Assert.Equal(formatter.BufferSize, derivedFormatter.BufferSize); } [Fact] public void BufferSize_RoundTrips() { Assert.Reflection.IntegerProperty( new MockBufferedMediaTypeFormatter(), c => c.BufferSize, expectedDefaultValue: 16 * 1024, minLegalValue: 0, illegalLowerValue: -1, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 1024); } [Fact] public void WriteToStreamAsync_WhenTypeParameterIsNull_ThrowsException() { BufferedMediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); Assert.ThrowsArgumentNull( () => formatter.WriteToStreamAsync(null, new object(), new MemoryStream(), null, null), "type"); } [Fact] public void WriteToStreamAsync_WhenStreamParameterIsNull_ThrowsException() { BufferedMediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); Assert.ThrowsArgumentNull( () => formatter.WriteToStreamAsync(typeof(object), new object(), null, null, null), "writeStream"); } [Fact] public void ReadFromStreamAsync_WhenTypeParamterIsNull_ThrowsException() { BufferedMediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => formatter.ReadFromStreamAsync(null, new MemoryStream(), null, null), "type"); } [Fact] public void ReadFromStreamAsync_WhenStreamParamterIsNull_ThrowsException() { BufferedMediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => formatter.ReadFromStreamAsync(typeof(object), null, null, null), "readStream"); } [Fact] public async Task BufferedWrite() { // Arrange. Specifically use the base class with async signatures. MediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); MemoryStream output = new MemoryStream(); // Act. Call the async signature. await formatter.WriteToStreamAsync(TestData.GetType(), TestData, output, null, null); // Assert byte[] expectedBytes = ExpectedSupportedEncodings.ElementAt(0).GetBytes(TestData); byte[] actualBytes = output.ToArray(); Assert.Equal(expectedBytes, actualBytes); } [Fact] public async Task BufferedRead() { // Arrange. Specifically use the base class with async signatures. MediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); byte[] expectedBytes = ExpectedSupportedEncodings.ElementAt(0).GetBytes(TestData); MemoryStream input = new MemoryStream(expectedBytes); // Act. Call the async signature. object result = await formatter.ReadFromStreamAsync(TestData.GetType(), input, null, null); // Assert Assert.Equal(TestData, result); } public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange MediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); string mediaType = string.Format("{0}; charset={1}", ExpectedSupportedMediaType, encoding); // Act & assert return ReadFromStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, content, mediaType, encoding, isDefaultEncoding); } public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange MediaTypeFormatter formatter = new MockBufferedMediaTypeFormatter(); string mediaType = string.Format("{0}; charset={1}", ExpectedSupportedMediaType, encoding); // Act & assert return WriteToStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, content, mediaType, encoding, isDefaultEncoding); } [Fact] public override Task Overridden_ReadFromStreamAsyncWithCancellationToken_GetsCalled() { // ReadFromStreamAsync is not overridable on BufferedMediaTypeFormatter. // So, let this test be a NOOP return TaskHelpers.Completed(); } [Fact] public override Task Overridden_ReadFromStreamAsyncWithoutCancellationToken_GetsCalled() { // ReadFromStreamAsync is not overridable on BufferedMediaTypeFormatter. // So, let this test be a NOOP return TaskHelpers.Completed(); } [Fact] public override Task Overridden_WriteToStreamAsyncWithCancellationToken_GetsCalled() { // WriteToStreamAsync is not overridable on BufferedMediaTypeFormatter. // So, let this test be a NOOP return TaskHelpers.Completed(); } [Fact] public override Task Overridden_WriteToStreamAsyncWithoutCancellationToken_GetsCalled() { // WriteToStreamAsync is not overridable on BufferedMediaTypeFormatter. // So, let this test be a NOOP return TaskHelpers.Completed(); } } public class MockBufferedMediaTypeFormatter : BufferedMediaTypeFormatter { private const string SupportedMediaType = "text/test"; public MockBufferedMediaTypeFormatter() { SupportedMediaTypes.Add(new MediaTypeHeaderValue(SupportedMediaType)); // Set default supported character encodings SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); SupportedEncodings.Add(new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true)); } public MockBufferedMediaTypeFormatter(MockBufferedMediaTypeFormatter formatter) : base(formatter) { } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } public override object ReadFromStream(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger) { object result = null; HttpContentHeaders contentHeaders = content == null ? null : content.Headers; Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders); using (StreamReader sReader = new StreamReader(stream, effectiveEncoding)) { if (type == typeof(SampleType)) { return new SampleType { Number = 42 }; } else { result = sReader.ReadToEnd(); } } return result; } public override void WriteToStream(Type type, object value, Stream stream, HttpContent content) { HttpContentHeaders contentHeaders = content == null ? null : content.Headers; Encoding effectiveEncoding = SelectCharacterEncoding(contentHeaders); using (StreamWriter sWriter = new StreamWriter(stream, effectiveEncoding)) { if (value != null) { sWriter.Write(value.ToString()); } else { sWriter.Write("null!"); } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/ContentNegotiationResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Formatting { public class ContentNegotiationResultTest { private readonly MediaTypeFormatter _formatter = new Mock().Object; private readonly MediaTypeHeaderValue _mediaType = new MediaTypeHeaderValue("app/json"); [Fact] public void Constructor_WhenFormatterParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => new ContentNegotiationResult(formatter: null, mediaType: null), "formatter"); } [Fact] public void MediaTypeProperty() { Assert.Reflection.Property(new ContentNegotiationResult(_formatter, _mediaType), nr => nr.MediaType, _mediaType, allowNull: true, roundTripTestValue: new MediaTypeHeaderValue("foo/bar")); } [Fact] public void FormatterProperty() { Assert.Reflection.Property(new ContentNegotiationResult(_formatter, _mediaType), nr => nr.Formatter, _formatter, allowNull: false, roundTripTestValue: new JsonMediaTypeFormatter()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/DataContractJsonMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Formatting.DataSets.Types; using System.Net.Http.Headers; using System.Runtime.Serialization.Json; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class DataContractJsonMediaTypeFormatter : JsonMediaTypeFormatter { public DataContractJsonMediaTypeFormatter() { UseDataContractJsonSerializer = true; } } public class DataContractJsonMediaTypeFormatterTests : MediaTypeFormatterTestBase { public static readonly TheoryDataSet AFewValidTypes = new() { typeof(bool), typeof(int), typeof(string), }; public override IEnumerable ExpectedSupportedMediaTypes { get { return HttpTestData.StandardJsonMediaTypes; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return ExpectedSupportedEncodings.ElementAt(0).GetBytes("{\"Number\":42}"); } } [Fact] public void DefaultMediaType_ReturnsApplicationJson() { MediaTypeHeaderValue mediaType = DataContractJsonMediaTypeFormatter.DefaultMediaType; Assert.NotNull(mediaType); Assert.Equal("application/json", mediaType.MediaType); } [Fact] public void Indent_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlMediaTypeFormatter(), c => c.Indent, expectedDefaultValue: false); } [Fact] public void MaxDepth_RoundTrips() { Assert.Reflection.IntegerProperty( new DataContractJsonMediaTypeFormatter(), c => c.MaxDepth, expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 256); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanReadType_ReturnsExpectedValues(Type variationType, object testData) { TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); bool isSerializable = IsTypeSerializableWithJsonSerializer(variationType, testData); bool canSupport = formatter.CanReadTypeProxy(variationType); // If we don't agree, we assert only if the DCJ serializer says it cannot support something we think it should Assert.False(isSerializable != canSupport && isSerializable, String.Format("CanReadType returned wrong value for '{0}'.", variationType)); // Ask a 2nd time to probe whether the cached result is treated the same canSupport = formatter.CanReadTypeProxy(variationType); Assert.False(isSerializable != canSupport && isSerializable, String.Format("2nd CanReadType returned wrong value for '{0}'.", variationType)); } [Theory] [PropertyData(nameof(AFewValidTypes))] public void CanWriteType_ReturnsFalse_ForValidTypes(Type type) { XmlMediaTypeFormatter formatter = new(); var canWrite = formatter.CanWriteType(type); #if Testing_NetStandard1_3 // Different behavior in netstandard1.3 due to no DataContract validation. Assert.False(canWrite); #else Assert.True(canWrite); #endif } public class InvalidDataContract { // removing the default ctor makes this invalid public InvalidDataContract(string s) { } } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Theory] [InlineData(typeof(IQueryable))] [InlineData(typeof(IEnumerable))] public async Task UseJsonFormatterWithNull(Type type) { JsonMediaTypeFormatter xmlFormatter = new DataContractJsonMediaTypeFormatter(); MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(type, null, memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("null"), "Using Json formatter to serialize null should emit 'null'."); } #endif [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "ValueAndRefTypeTestDataCollectionExceptULong", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync(Type variationType, object testData) { // Guard bool canSerialize = IsTypeSerializableWithJsonSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } [Theory] [TestDataSet(typeof(XmlMediaTypeFormatterTests), "BunchOfTypedObjectsTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_KnownTypes(Type variationType, object testData) { // Guard bool canSerialize = IsTypeSerializableWithJsonSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter { AddDBNullKnownType = true, }; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } #if Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_UsingDataContractSerializer_Throws(Type variationType, object testData) { // Arrange. First, get some data using XmlSerializer. bool canSerialize = IsTypeSerializableWithJsonSerializer(variationType, testData, actuallyCheck: true) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { var formatter = new JsonMediaTypeFormatter(); using var stream = new MemoryStream(); using var content = new StringContent(string.Empty); await formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null); await stream.FlushAsync(); stream.Position = 0L; content.Headers.ContentLength = stream.Length; formatter.UseDataContractJsonSerializer = true; // Act & Assert await Assert.ThrowsAsync(() => formatter.ReadFromStreamAsync(variationType, stream, content, formatterLogger: null), "Unable to validate types on this platform when UseDataContractJsonSerializer is 'true'. " + "Please reset UseDataContractJsonSerializer or move to a supported platform, one where the " + ".NET Standard 2.0 assembly is usable."); } } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task WriteToStreamAsync_UsingDataContractSerializer_Throws(Type variationType, object testData) { // Arrange var formatter = new JsonMediaTypeFormatter() { UseDataContractJsonSerializer = true}; using var stream = new MemoryStream(); using var content = new StringContent(string.Empty); // Act & Assert await Assert.ThrowsAsync(() => formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null), "Unable to validate types on this platform when UseDataContractJsonSerializer is 'true'. " + "Please reset UseDataContractJsonSerializer or move to a supported platform, one where the " + ".NET Standard 2.0 assembly is usable."); } #else #if !NETCOREAPP2_1 // DBNull not serializable on .NET Core 2.1. // Test alternate null value [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); Type variationType = typeof(DBNull); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value round-trips as either Object or DBNull because serialization includes its type Assert.Equal(testData, readObj); } [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsEmptyString() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter { AddDBNullKnownType = true, }; Type variationType = typeof(string); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // Lower levels convert DBNull.Value to empty string on read Assert.Equal(String.Empty, readObj); } #endif [Fact] public async Task UseDataContractJsonSerializer_Default() { DataContractJsonMediaTypeFormatter jsonFormatter = new DataContractJsonMediaTypeFormatter(); MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(jsonFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.False(serializedString.Contains("\r\n"), "Using DCJS should emit data without indentation by default."); } #endif [Fact] public void UseDataContractJsonSerializer_True_Indent_Throws() { DataContractJsonMediaTypeFormatter jsonFormatter = new DataContractJsonMediaTypeFormatter { Indent = true }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); Assert.Throws( () => jsonFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); } #if Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public override Task Overridden_ReadFromStreamAsyncWithCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_ReadFromStreamAsyncWithoutCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_WriteToStreamAsyncWithCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_WriteToStreamAsyncWithoutCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task ReadFromStreamAsync_ReadsDataButDoesNotCloseStream() { return Task.CompletedTask; } // Attributes are in base class. public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { return Task.CompletedTask; } [Fact] public override Task ReadFromStreamAsync_WhenContentLengthIsNull_ReadsDataButDoesNotCloseStream() { return Task.CompletedTask; } // Attributes are in base class. public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { return Task.CompletedTask; } [Fact] public override Task WriteToStreamAsync_WhenObjectIsNull_WritesDataButDoesNotCloseStream() { return Task.CompletedTask; } [Fact] public override Task WriteToStreamAsync_WritesDataButDoesNotCloseStream() { return Task.CompletedTask; } #else public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange DataContractJsonMediaTypeFormatter formatter = new DataContractJsonMediaTypeFormatter(); string formattedContent = "\"" + content + "\""; string mediaType = string.Format("application/json; charset={0}", encoding); // Act & assert return ReadContentUsingCorrectCharacterEncodingHelperAsync( formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange DataContractJsonMediaTypeFormatter formatter = new DataContractJsonMediaTypeFormatter(); string formattedContent = "\"" + content + "\""; string mediaType = string.Format("application/json; charset={0}", encoding); // Act & assert return WriteContentUsingCorrectCharacterEncodingHelperAsync( formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } #endif public class TestJsonMediaTypeFormatter : DataContractJsonMediaTypeFormatter { public bool AddDBNullKnownType { get; set; } public bool CanReadTypeProxy(Type type) { return CanReadType(type); } public bool CanWriteTypeProxy(Type type) { return CanWriteType(type); } public override DataContractJsonSerializer CreateDataContractSerializer(Type type) { if (AddDBNullKnownType) { return new DataContractJsonSerializer(type, new Type[] { typeof(DBNull), }); } else { return base.CreateDataContractSerializer(type); } } } private bool IsTypeSerializableWithJsonSerializer(Type type, object obj, bool actuallyCheck = false) { #if Testing_NetStandard1_3 // Different behavior in netstandard1.3 due to no DataContract validation. if (!actuallyCheck) { return false; } #endif try { new DataContractJsonSerializer(type); if (obj != null && obj.GetType() != type) { new DataContractJsonSerializer(obj.GetType()); } } catch { return false; } return !Assert.Http.IsKnownUnserializable(type, obj, (t) => typeof(INotJsonSerializable).IsAssignableFrom(t)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/DefaultContentNegotiatorTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Text; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class DefaultContentNegotiatorTests { private readonly DefaultContentNegotiator _negotiator = new DefaultContentNegotiator(); private readonly HttpRequestMessage _request = new HttpRequestMessage(); public static TheoryDataSet MatchRequestMediaTypeData { get { // string requestMediaType, string[] supportedMediaTypes, string expectedMediaType return new TheoryDataSet { { "text/plain", new string[0], null }, { "text/plain", new string[] { "text/xml", "application/xml" }, null }, { "application/xml", new string[] { "application/xml", "text/xml" }, "application/xml" }, { "APPLICATION/XML", new string[] { "text/xml", "application/xml" }, "application/xml" }, { "application/xml; charset=utf-8", new string[] { "text/xml", "application/xml" }, "application/xml" }, { "application/xml; charset=utf-8; parameter=value", new string[] { "text/xml", "application/xml" }, "application/xml" }, }; } } public static TheoryDataSet MatchAcceptHeaderData { get { // string[] acceptHeader, string[] supportedMediaTypes, string expectedMediaType, double matchQuality, int range return new TheoryDataSet { { new string[] { "text/plain" }, new string[0], null, 0.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "text/plain" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "text/plain; q=0.5" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "application/xml" }, new string[] { "application/xml", "text/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "APPLICATION/XML; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "text/xml; q=0.5", "APPLICATION/XML; q=0.7" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.7, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "application/xml; q=0.0" }, new string[] { "application/xml", "text/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "text/xml; q=0.0", "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "text/*" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange }, { new string[] { "text/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "text/*", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange }, { new string[] { "text/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange }, { new string[] { "text/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "text/*; q=0.0", "application/xml; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "text/*; q=0.0", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "*/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange }, { new string[] { "*/*; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None }, { new string[] { "*/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "*/*; q=1.0", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange }, { new string[] { "*/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "text/*; q=0.5", "*/*; q=0.2", "application/xml; q=1.0" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, { new string[] { "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral }, }; } } public static TheoryDataSet ShouldMatchOnTypeData { get { // bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult return new TheoryDataSet { { false, new string[0], true }, { true, new string[0], true }, { false, new string[] { "application/xml" }, true }, { true, new string[] { "application/xml" }, false }, { false, new string[] { "application/xml; q=1.0" }, true }, { true, new string[] { "application/xml; q=1.0" }, false }, { false, new string[] { "application/xml; q=0.0" }, true }, { true, new string[] { "application/xml; q=0.0" }, false }, { false, new string[] { "application/xml; q=0.0", "application/json" }, true }, { true, new string[] { "application/xml; q=0.0", "application/json" }, false }, { false, new string[] { "text/nomatch" }, true }, { true, new string[] { "text/nomatch" }, false }, }; } } public static TheoryDataSet MatchTypeData { get { // string[] supportedMediaTypes, string expectedMediaType return new TheoryDataSet { { new string[0], "application/octet-stream" }, { new string[] { "text/xml", "application/xml" }, "text/xml" }, { new string[] { "application/xml", "text/xml" }, "application/xml" }, }; } } public static TheoryDataSet SelectResponseCharacterEncodingData { get { // string[] acceptEncodings, string requestEncoding, string[] supportedEncodings, string expectedEncoding return new TheoryDataSet { { new string[] { "utf-8" }, null, new string[0], null }, { new string[0], "utf-8", new string[0], null }, { new string[0], null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { new string[0], "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" }, { new string[] { "utf-8" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { new string[] { "utf-16" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" }, { new string[] { "utf-16; q=0.5" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" }, { new string[] { "utf-8; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { new string[] { "utf-8; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" }, { new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" }, { new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { new string[] { "*; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { new string[] { "*; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" }, }; } } public static TheoryDataSet, MediaTypeFormatterMatch> SelectResponseMediaTypeData { get { // Only mapping and accept makes sense with q != 1.0 MediaTypeFormatterMatch matchMapping10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping); MediaTypeFormatterMatch matchMapping05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping); MediaTypeFormatterMatch matchAccept10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral); MediaTypeFormatterMatch matchAccept05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral); MediaTypeFormatterMatch matchAcceptSubTypeRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange); MediaTypeFormatterMatch matchAcceptSubTypeRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange); MediaTypeFormatterMatch matchAcceptAllRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange); MediaTypeFormatterMatch matchAcceptAllRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange); MediaTypeFormatterMatch matchRequest10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestMediaType); MediaTypeFormatterMatch matchType10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnCanWriteType); // ICollection candidateMatches, MediaTypeFormatterMatch winner return new TheoryDataSet, MediaTypeFormatterMatch> { { new List(), null }, { new List() { matchType10 }, matchType10 }, { new List() { matchType10, matchRequest10 }, matchRequest10 }, { new List() { matchType10, matchRequest10, matchAcceptAllRange10 }, matchAcceptAllRange10 }, { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 }, { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10, matchAccept10 }, matchAccept10 }, { new List() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10, matchAccept10, matchMapping10 }, matchMapping10 }, { new List() { matchAccept05, matchAccept10 }, matchAccept10 }, { new List() { matchAccept10, matchAccept05 }, matchAccept10 }, { new List() { matchAcceptSubTypeRange05, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 }, { new List() { matchAcceptSubTypeRange10, matchAcceptSubTypeRange05 }, matchAcceptSubTypeRange10 }, { new List() { matchAcceptAllRange05, matchAcceptAllRange10 }, matchAcceptAllRange10 }, { new List() { matchAcceptAllRange10, matchAcceptAllRange05 }, matchAcceptAllRange10 }, { new List() { matchMapping05, matchMapping10 }, matchMapping10 }, { new List() { matchMapping10, matchMapping05 }, matchMapping10 }, { new List() { matchMapping05, matchAccept05 }, matchMapping05 }, { new List() { matchMapping10, matchAccept10 }, matchMapping10 }, { new List() { matchMapping05, matchAcceptSubTypeRange05 }, matchMapping05 }, { new List() { matchMapping10, matchAcceptSubTypeRange10 }, matchMapping10 }, { new List() { matchMapping05, matchAcceptAllRange05 }, matchMapping05 }, { new List() { matchMapping10, matchAcceptAllRange10 }, matchMapping10 }, { new List() { matchMapping05, matchAccept10 }, matchAccept10 }, { new List() { matchMapping05, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 }, { new List() { matchMapping05, matchAcceptAllRange10 }, matchAcceptAllRange10 }, }; } } public static TheoryDataSet UpdateBestMatchData { get { MediaTypeFormatterMatch matchMapping10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.None); MediaTypeFormatterMatch matchMapping05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.None); // MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement, currentWins return new TheoryDataSet { { null, matchMapping10, false }, { null, matchMapping05, false }, { matchMapping10, matchMapping10, true }, { matchMapping10, matchMapping05, true }, { matchMapping05, matchMapping10, false }, { matchMapping05, matchMapping05, true }, }; } } private static MediaTypeFormatterMatch CreateMatch(double? quality, MediaTypeFormatterMatchRanking ranking) { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("text/test"); return new MediaTypeFormatterMatch(formatter, mediaType, quality, ranking); } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(DefaultContentNegotiator), TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void Negotiate_WhenTypeParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(null, _request, Enumerable.Empty()), "type"); } [Fact] public void Negotiate_WhenRequestParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(typeof(string), null, Enumerable.Empty()), "request"); } [Fact] public void Negotiate_WhenFormattersParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(typeof(string), _request, null), "formatters"); } [Fact] public void Negotiate_ForEmptyFormatterCollection_ReturnsNull() { var result = _negotiator.Negotiate(typeof(string), _request, Enumerable.Empty()); Assert.Null(result); } [Fact] public void Negotiate_MediaTypeMappingTakesPrecedenceOverAcceptHeader() { // Prepare the request message _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); _request.Headers.Add("Browser", "IE"); _request.Headers.Add("Cookie", "ABC"); // Prepare the formatters List formatters = new List(); formatters.Add(new JsonMediaTypeFormatter()); formatters.Add(new XmlMediaTypeFormatter()); PlainTextFormatter frmtr = new PlainTextFormatter(); frmtr.SupportedMediaTypes.Clear(); frmtr.MediaTypeMappings.Clear(); frmtr.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml")); frmtr.MediaTypeMappings.Add(new MyMediaTypeMapping(new MediaTypeHeaderValue(("application/xml")))); formatters.Add(frmtr); // Act var result = _negotiator.Negotiate(typeof(string), _request, formatters); // Assert Assert.NotNull(result); Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_ForRequestReturnsFirstMatchingFormatter() { MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/myMediaType"); MediaTypeFormatter formatter1 = new MockMediaTypeFormatter() { CanWriteTypeCallback = (Type t) => false }; MediaTypeFormatter formatter2 = new MockMediaTypeFormatter() { CanWriteTypeCallback = (Type t) => true }; formatter2.SupportedMediaTypes.Add(mediaType); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection( new MediaTypeFormatter[] { formatter1, formatter2 }); _request.Content = new StringContent("test", Encoding.Default, mediaType.MediaType); var result = _negotiator.Negotiate(typeof(string), _request, collection); Assert.Same(formatter2, result.Formatter); Assert.MediaType.AreEqual(mediaType, result.MediaType, "Expected the formatter's media type to be returned."); } [Fact] public void Negotiate_SelectsJsonAsDefaultFormatter() { // Arrange _request.Content = new StringContent("test"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.IsType(result.Formatter); Assert.Equal(MediaTypeConstants.ApplicationJsonMediaType.MediaType, result.MediaType.MediaType); } [Fact] public void Negotiate_SelectsXmlFormatter_ForXhrRequestThatAcceptsXml() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXhrRequestThatDoesNotSpecifyAcceptHeaders() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_RespectsFormatterOrdering_ForXhrRequestThatDoesNotSpecifyAcceptHeaders() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); MediaTypeFormatterCollection formatters = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { new XmlMediaTypeFormatter(), new JsonMediaTypeFormatter(), new FormUrlEncodedMediaTypeFormatter() }); // Act var result = _negotiator.Negotiate(typeof(string), _request, formatters); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXHRAndJsonValueResponse() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(JToken), _request, new MediaTypeFormatterCollection()); Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXHRAndMatchAllAcceptHeader() { // Accept _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_UsesRequestedFormatterForXHRAndMatchAllPlusOtherAcceptHeader() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); _request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); // XHR header sent by Firefox 3b5 // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Theory] [InlineData(true)] [InlineData(false)] public void Negotiate_ObservesExcludeMatchOnTypeOnly(bool excludeMatchOnTypeOnly) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnTypeOnly); _request.Content = new StringContent("test"); _request.Headers.Accept.ParseAdd("text/html"); // Act var result = negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert if (excludeMatchOnTypeOnly) { Assert.Null(result); } else { Assert.NotNull(result); Assert.Equal("application/json", result.MediaType.MediaType); } } [Fact] public void ComputeFormatterMatches_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); Type type = typeof(object); HttpRequestMessage request = new HttpRequestMessage(); List formatters = new List(); Assert.ThrowsArgumentNull(() => negotiator.ComputeFormatterMatches(type: null, request: request, formatters: formatters), "type"); Assert.ThrowsArgumentNull(() => negotiator.ComputeFormatterMatches(type: type, request: null, formatters: formatters), "request"); Assert.ThrowsArgumentNull(() => negotiator.ComputeFormatterMatches(type: type, request: request, formatters: null), "formatters"); } [Fact] public void MatchMediaTypeMapping_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => negotiator.MatchMediaTypeMapping(request: null, formatter: formatter), "request"); Assert.ThrowsArgumentNull(() => negotiator.MatchMediaTypeMapping(request: request, formatter: null), "formatter"); } [Fact] public void MatchMediaTypeMapping_ReturnsMatch() { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); MediaTypeHeaderValue mappingMediatype = MediaTypeHeaderValue.Parse("application/other"); MockMediaTypeMapping mockMediaTypeMapping = new MockMediaTypeMapping(mappingMediatype, 0.75); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); formatter.MediaTypeMappings.Add(mockMediaTypeMapping); // Act MediaTypeFormatterMatch match = negotiator.MatchMediaTypeMapping(request, formatter); // Assert Assert.True(mockMediaTypeMapping.WasInvoked); Assert.Same(request, mockMediaTypeMapping.Request); Assert.Same(formatter, match.Formatter); Assert.Equal(mockMediaTypeMapping.MediaType, match.MediaType); Assert.Equal(mockMediaTypeMapping.MatchQuality, match.Quality); Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnRequestWithMediaTypeMapping, match.Ranking); } [Fact] public void MatchAcceptHeader_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); List sortedAcceptValues = new List(); Assert.ThrowsArgumentNull(() => negotiator.MatchAcceptHeader(sortedAcceptValues: null, formatter: formatter), "sortedAcceptValues"); Assert.ThrowsArgumentNull(() => negotiator.MatchAcceptHeader(sortedAcceptValues: sortedAcceptValues, formatter: null), "formatter"); } [Theory] [PropertyData("MatchAcceptHeaderData")] public void MatchAcceptHeader_ReturnsMatch(string[] acceptHeaders, string[] supportedMediaTypes, string expectedMediaType, double expectedQuality, int ranking) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); List unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList(); IEnumerable sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); foreach (string supportedMediaType in supportedMediaTypes) { formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType)); } // Act MediaTypeFormatterMatch match = negotiator.MatchAcceptHeader(sortedAcceptHeaders, formatter); // Assert if (expectedMediaType == null) { Assert.Null(match); } else { Assert.Same(formatter, match.Formatter); Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType); Assert.Equal(expectedQuality, match.Quality); Assert.Equal(ranking, (int)match.Ranking); } } [Fact] public void MatchRequestMediaType_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => negotiator.MatchRequestMediaType(request: null, formatter: formatter), "request"); Assert.ThrowsArgumentNull(() => negotiator.MatchRequestMediaType(request: request, formatter: null), "formatter"); } [Theory] [PropertyData("MatchRequestMediaTypeData")] public void MatchRequestMediaType_ReturnsMatch(string requestMediaType, string[] supportedMediaTypes, string expectedMediaType) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); request.Content = new StringContent(String.Empty); request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(requestMediaType); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); foreach (string supportedMediaType in supportedMediaTypes) { formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType)); } // Act MediaTypeFormatterMatch match = negotiator.MatchRequestMediaType(request, formatter); // Assert if (expectedMediaType == null) { Assert.Null(match); } else { Assert.Same(formatter, match.Formatter); Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType); Assert.Equal(1.0, match.Quality); Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnRequestMediaType, match.Ranking); } } [Fact] public void ShouldMatchOnType_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); Assert.ThrowsArgumentNull(() => negotiator.ShouldMatchOnType(sortedAcceptValues: null), "sortedAcceptValues"); } [Theory] [PropertyData("ShouldMatchOnTypeData")] public void ShouldMatchOnType_ReturnsExpectedResult(bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnType); List unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList(); IEnumerable sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders); // Act bool result = negotiator.ShouldMatchOnType(sortedAcceptHeaders); // Assert Assert.Equal(expectedResult, result); } [Fact] public void MatchType_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); Type type = typeof(object); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => negotiator.MatchType(type: null, formatter: formatter), "type"); Assert.ThrowsArgumentNull(() => negotiator.MatchType(type: type, formatter: null), "formatter"); } [Theory] [PropertyData("MatchTypeData")] public void MatchType_ReturnsMatch(string[] supportedMediaTypes, string expectedMediaType) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); foreach (string supportedMediaType in supportedMediaTypes) { formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType)); } // Act MediaTypeFormatterMatch match = negotiator.MatchType(typeof(object), formatter); // Assert Assert.Same(formatter, match.Formatter); Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType); Assert.Equal(1.0, match.Quality); Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnCanWriteType, match.Ranking); } [Fact] public void SelectResponseMediaTypeFormatter_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => negotiator.SelectResponseMediaTypeFormatter(matches: null), "matches"); } [Theory] [PropertyData("SelectResponseMediaTypeData")] public void SelectResponseMediaTypeFormatter_SelectsMediaType(ICollection matches, MediaTypeFormatterMatch expectedWinner) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); // Act MediaTypeFormatterMatch actualWinner = negotiator.SelectResponseMediaTypeFormatter(matches); // Assert Assert.Same(expectedWinner, actualWinner); } [Fact] public void SelectResponseCharacterEncoding_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => negotiator.SelectResponseCharacterEncoding(request: null, formatter: formatter), "request"); Assert.ThrowsArgumentNull(() => negotiator.SelectResponseCharacterEncoding(request: request, formatter: null), "formatter"); } [Theory] [PropertyData("SelectResponseCharacterEncodingData")] public void SelectResponseCharacterEncoding_SelectsEncoding(string[] acceptCharsetHeaders, string requestEncoding, string[] supportedEncodings, string expectedEncoding) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); HttpRequestMessage request = new HttpRequestMessage(); foreach (string acceptCharsetHeader in acceptCharsetHeaders) { request.Headers.AcceptCharset.Add(StringWithQualityHeaderValue.Parse(acceptCharsetHeader)); } if (requestEncoding != null) { Encoding reqEncoding = Encoding.GetEncoding(requestEncoding); StringContent content = new StringContent("", reqEncoding, "text/plain"); request.Content = content; } MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true }; foreach (string supportedEncoding in supportedEncodings) { formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding)); } // Act Encoding actualEncoding = negotiator.SelectResponseCharacterEncoding(request, formatter); // Assert if (expectedEncoding == null) { Assert.Null(actualEncoding); } else { Assert.Equal(Encoding.GetEncoding(expectedEncoding), actualEncoding); } } [Fact] public void SortMediaTypeWithQualityHeaderValuesByQFactor_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); Assert.ThrowsArgumentNull(() => negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor((HttpHeaderValueCollection)null), "headerValues"); } [Theory] [TestDataSet(typeof(MediaTypeWithQualityHeaderValueComparerTests), "BeforeAfterSortedValues")] public void SortMediaTypeWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable unsorted, IEnumerable expectedSorted) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); List unsortedValues = new List(unsorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u))); List expectedSortedValues = new List(expectedSorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u))); // Act IEnumerable actualSorted = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedValues); // Assert Assert.True(expectedSortedValues.SequenceEqual(actualSorted)); } [Fact] public void SortStringWithQualityHeaderValuesByQFactor_ThrowsOnNull() { MockContentNegotiator negotiator = new MockContentNegotiator(); Assert.ThrowsArgumentNull(() => negotiator.SortStringWithQualityHeaderValuesByQFactor((HttpHeaderValueCollection)null), "headerValues"); } [Theory] [TestDataSet(typeof(StringWithQualityHeaderValueComparerTests), "BeforeAfterSortedValues")] public void SortStringWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable unsorted, IEnumerable expectedSorted) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); List unsortedValues = new List(unsorted.Select(u => StringWithQualityHeaderValue.Parse(u))); List expectedSortedValues = new List(expectedSorted.Select(u => StringWithQualityHeaderValue.Parse(u))); // Act IEnumerable actualSorted = negotiator.SortStringWithQualityHeaderValuesByQFactor(unsortedValues); // Assert Assert.True(expectedSortedValues.SequenceEqual(actualSorted)); } [Theory] [PropertyData("UpdateBestMatchData")] public void UpdateBestMatch_SelectsCorrectly(MediaTypeFormatterMatch current, MediaTypeFormatterMatch replacement, bool currentWins) { // Arrange MockContentNegotiator negotiator = new MockContentNegotiator(); // Act MediaTypeFormatterMatch actualResult = negotiator.UpdateBestMatch(current, replacement); // Assert if (currentWins) { Assert.Same(current, actualResult); } else { Assert.Same(replacement, actualResult); } } private class PlainTextFormatter : MediaTypeFormatter { public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } private class MyMediaTypeMapping : MediaTypeMapping { public MyMediaTypeMapping(MediaTypeHeaderValue mediaType) : base(mediaType) { } public override double TryMatchMediaType(HttpRequestMessage request) { if (request.Headers.Contains("Cookie")) { return 1.0; } else { return 0; } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/FormDataCollectionTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class FormDataCollectionTests { [Fact] public void CreateFromUri() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1&y=2")); Assert.Equal("1", form.Get("x")); Assert.Equal("2", form.Get("y")); } [Fact] public void IndexerIsEquivalentToGet() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1&y=2")); Assert.Equal("1", form.Get("x")); Assert.Equal(form["x"], form.Get("x")); Assert.Equal(form[null], form.Get(null)); } [Fact] public void CreateFromEmptyUri() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com")); Assert.Empty(form); } [Fact] public void UriConstructorThrowsNull() { Assert.ThrowsArgumentNull(() => new FormDataCollection((Uri)null), "uri"); } [Fact] public void CreateFromEmptyString() { FormDataCollection form = new FormDataCollection(""); Assert.Empty(form); } [Fact] public void CreateFromNullString() { FormDataCollection form = new FormDataCollection((string)null); Assert.Empty(form); } [Fact] public void PairConstructorThrowsNull() { var arg = (IEnumerable>)null; Assert.ThrowsArgumentNull(() => new FormDataCollection(arg), "pairs"); } [Fact] public void CreateFromPairs() { Dictionary pairs = new Dictionary { { "x", "1"}, { "y" , "2"} }; var form = new FormDataCollection(pairs); Assert.Equal("1", form.Get("x")); Assert.Equal("2", form.Get("y")); } [Fact] public void Enumeration() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1&y=2")); // Enumeration should be ordered String s = ""; foreach (KeyValuePair kv in form) { s += string.Format("{0}={1};", kv.Key, kv.Value); } Assert.Equal("x=1;y=2;", s); } [Fact] public void GetValues() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1&x=2&x=3")); Assert.Equal(new string[] { "1", "2", "3" }, form.GetValues("x")); } [Fact] public void CaseInSensitive() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1&Y=2")); var nvc = form.ReadAsNameValueCollection(); Assert.Equal(2, nvc.Count); Assert.Equal("1", nvc.Get("x")); Assert.Equal("2", nvc.Get("y")); } [Fact] public void ToNameValueCollection() { FormDataCollection form = new FormDataCollection(new Uri("http://foo.com/?x=1a&y=2&x=1b&=ValueOnly&KeyOnly")); var nvc = form.ReadAsNameValueCollection(); // y=2 // x=1a;x=1b // =ValueOnly // KeyOnly Assert.Equal(4, nvc.Count); Assert.Equal(new string[] { "1a", "1b" }, nvc.GetValues("x")); Assert.Equal("1a,1b", nvc.Get("x")); Assert.Equal("2", nvc.Get("y")); Assert.Equal("", nvc.Get("KeyOnly")); Assert.Equal("ValueOnly", nvc.Get("")); } const string SPACE = " "; // single literal space character [Theory] [InlineData("x=?", "?")] // normal [InlineData("x=%3f", "?")] // normal [InlineData("x=%3d", "=")] // normal [InlineData("x=abc", "abc")] // normal [InlineData("x", "")] // key only [InlineData("x=", "")] // rhs only [InlineData("x=%20", SPACE)] // escaped space [InlineData("x=" + SPACE, SPACE)] // literal space [InlineData("x=+", SPACE)] [InlineData("x=null", "null")] // null literal, not escaped [InlineData("x=undefined", "undefined")] // undefined literal, not escaped [InlineData("x=\"null\"", "\"null\"")] // quoted null, preserved as is public void Whitespace(string queryString, string expected) { FormDataCollection fd = new FormDataCollection(queryString); Assert.Single(fd); Assert.Equal(expected, fd.Get("x")); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromContentTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Formatting.Parsers; using System.Text; using System.Web.Http; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class FormUrlEncodedJsonFromContentTests { public static TheoryDataSet TestEncodedNameTestData { get { // string: encoded string: result return new TheoryDataSet { { "some+thing=10", @"{""some thing"":""10""}" }, { "%E5%B8%A6%E4%B8%89%E4%B8%AA%E8%A1%A8=bar", @"{""带三个表"":""bar""}" }, { "some+thing=10&%E5%B8%A6%E4%B8%89%E4%B8%AA%E8%A1%A8=bar", @"{""some thing"":""10"",""带三个表"":""bar""}"}, { "a[0\r\n][b]=1", "{\"a\":{\"0\\r\\n\":{\"b\":\"1\"}}}" }, { "a[0%0d\n][b]=1", "{\"a\":{\"0\\r\\n\":{\"b\":\"1\"}}}" }, { "a[0%0d%0a][b]=1", "{\"a\":{\"0\\r\\n\":{\"b\":\"1\"}}}" }, { "a[0\0]=1", "{\"a\":{\"0\\u0000\":\"1\"}}" }, { "a[0%00]=1", "{\"a\":{\"0\\u0000\":\"1\"}}" }, { "a[\00]=1", "{\"a\":{\"\\u00000\":\"1\"}}" }, { "a[%000]=1", "{\"a\":{\"\\u00000\":\"1\"}}" }, }; } } public static TheoryDataSet TestObjectTestData { get { // string: encoded string: result return new TheoryDataSet { { "a[]=4&a[]=5&b[x][]=7&b[y]=8&b[z][]=9&b[z][]=true&b[z][]=undefined&b[z][]=&c=1&f=", @"{""a"":[""4"",""5""],""b"":{""x"":[""7""],""y"":""8"",""z"":[""9"",""true"",""undefined"",""""]},""c"":""1"",""f"":""""}" }, { "customer[Name]=Pete&customer[Address]=Redmond&customer[Age][0][]=23&customer[Age][0][]=24&customer[Age][1][]=25&" + "customer[Age][1][]=26&customer[Phones][]=425+888+1111&customer[Phones][]=425+345+7777&customer[Phones][]=425+888+4564&" + "customer[EnrolmentDate]=%22%5C%2FDate(1276562539537)%5C%2F%22&role=NewRole&changeDate=3&count=15", @"{""customer"":{""Name"":""Pete"",""Address"":""Redmond"",""Age"":[[""23"",""24""],[""25"",""26""]]," + @"""Phones"":[""425 888 1111"",""425 345 7777"",""425 888 4564""],""EnrolmentDate"":""\""\\/Date(1276562539537)\\/\""""},""role"":""NewRole"",""changeDate"":""3"",""count"":""15""}" }, { "customers[0][Name]=Pete2&customers[0][Address]=Redmond2&customers[0][Age][0][]=23&customers[0][Age][0][]=24&" + "customers[0][Age][1][]=25&customers[0][Age][1][]=26&customers[0][Phones][]=425+888+1111&customers[0][Phones][]=425+345+7777&" + "customers[0][Phones][]=425+888+4564&customers[0][EnrolmentDate]=%22%5C%2FDate(1276634840700)%5C%2F%22&customers[1][Name]=Pete3&" + "customers[1][Address]=Redmond3&customers[1][Age][0][]=23&customers[1][Age][0][]=24&customers[1][Age][1][]=25&customers[1][Age][1][]=26&" + "customers[1][Phones][]=425+888+1111&customers[1][Phones][]=425+345+7777&customers[1][Phones][]=425+888+4564&customers[1][EnrolmentDate]=%22%5C%2FDate(1276634840700)%5C%2F%22", @"{""customers"":[{""Name"":""Pete2"",""Address"":""Redmond2"",""Age"":[[""23"",""24""],[""25"",""26""]]," + @"""Phones"":[""425 888 1111"",""425 345 7777"",""425 888 4564""],""EnrolmentDate"":""\""\\/Date(1276634840700)\\/\""""}," + @"{""Name"":""Pete3"",""Address"":""Redmond3"",""Age"":[[""23"",""24""],[""25"",""26""]],""Phones"":[""425 888 1111"",""425 345 7777"",""425 888 4564""],""EnrolmentDate"":""\""\\/Date(1276634840700)\\/\""""}]}" }, { "ab%5B%5D=hello", @"{""ab"":[""hello""]}" }, { "123=hello", @"{""123"":""hello""}" }, { "a%5B%5D=1&a", @"{""a"":[""1"",""""]}" }, { "a=1&a", @"{""a"":[""1"",""""]}" } }; } } [Theory, InlineData("abc", "{\"abc\":\"\"}"), InlineData("%2eabc%2e", "{\".abc.\":\"\"}"), InlineData("", "{}"), InlineData("a=1", "{\"a\":\"1\"}")] public void SimpleStringsTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a=2", "{\"a\":\"2\"}"), InlineData("b=true", "{\"b\":\"true\"}"), InlineData("c=hello", "{\"c\":\"hello\"}"), InlineData("d=", "{\"d\":\"\"}"), InlineData("e=null", "{\"e\":\"null\"}")] public void SimpleObjectsTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Fact] public void LegacyArraysTest() { ValidateFormUrlEncoded("a=1&a=hello&a=333", "{\"a\":[\"1\",\"hello\",\"333\"]}"); // Only valid in shallow serialization ParseInvalidFormUrlEncoded("a[z]=2&a[z]=3"); } [Theory, InlineData("a[]=1&a[]=hello&a[]=333", "{\"a\":[\"1\",\"hello\",\"333\"]}"), InlineData("a[b][]=1&a[b][]=hello&a[b][]=333", "{\"a\":{\"b\":[\"1\",\"hello\",\"333\"]}}"), InlineData("a[]=", "{\"a\":[\"\"]}"), InlineData("a%5B%5D=2", @"{""a"":[""2""]}"), InlineData("a[x][0]=1&a[x][]=2", @"{""a"":{""x"":[""1"",""2""]}}")] public void ArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[0][]=1&a[0][]=hello&a[1][]=333", "{\"a\":[[\"1\",\"hello\"],[\"333\"]]}"), InlineData("a[b][0][]=1&a[b][1][]=hello&a[b][1][]=333", "{\"a\":{\"b\":[[\"1\"],[\"hello\",\"333\"]]}}"), InlineData("a[0][0][0][]=1", "{\"a\":[[[[\"1\"]]]]}")] public void MultidimensionalArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[0][]=hello&a[2][]=333", "{\"a\":{\"0\":[\"hello\"],\"2\":[\"333\"]}}"), InlineData("a[0]=hello", "{\"a\":[\"hello\"]}"), InlineData("a[1][]=hello", "{\"a\":{\"1\":[\"hello\"]}}"), InlineData("a[1][0]=hello", "{\"a\":{\"1\":[\"hello\"]}}")] public void SparseArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("b[]=2&b[1][c]=d", "{\"b\":[\"2\",{\"c\":\"d\"}]}")] public void ArraysWithMixedMembers(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("=3", "{\"\":\"3\"}"), InlineData("a=1&=3", "{\"a\":\"1\",\"\":\"3\"}"), InlineData("=3&b=2", "{\"\":\"3\",\"b\":\"2\"}")] public void EmptyKeyTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[b]=1&a=2"), InlineData("a[b]=1&a[b][]=2"), InlineData("a[x][]=1&a[x][0]=2"), InlineData("a=2&a[b]=1"), InlineData("[]=1"), InlineData("a[][]=0"), InlineData("a[][x]=0"), InlineData("a&a[b]=1")] public void InvalidObjectGraphsTest(string encoded) { ParseInvalidFormUrlEncoded(encoded); } [Theory, InlineData("a[b=2"), InlineData("a[[b]=2"), InlineData("a[b]]=2")] public void InvalidFormUrlEncodingTest(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing form-urlencoded data originated from JS primitives. /// [Theory, InlineData("abc", @"{""abc"":""""}"), InlineData("123", @"{""123"":""""}"), InlineData("true", @"{""true"":""""}"), InlineData("", "{}"), InlineData("%2fabc%2f", @"{""/abc/"":""""}")] public void TestJValue(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded data originated from JS primitives. /// [Theory, InlineData("a[b]=1&a=2"), InlineData("a=2&a[b]=1"), InlineData("[]=1")] public void TestJValueNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing form-urlencoded data originated from JS objects. /// [Theory, InlineData("a=NaN", @"{""a"":""NaN""}"), InlineData("a=false", @"{""a"":""false""}"), InlineData("a=foo", @"{""a"":""foo""}"), InlineData("1=1", "{\"1\":\"1\"}")] public void TestObjects(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data originated from JS arrays. /// [Theory, InlineData("a[]=2", @"{""a"":[""2""]}"), InlineData("a[]=", @"{""a"":[""""]}"), InlineData("a[0][0][]=1", @"{""a"":[[[""1""]]]}"), InlineData("z[]=9&z[]=true&z[]=undefined&z[]=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z[]=9&z[]=true&z[]=undefined&z[]=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z[0][]=9&z[0][]=true&z[1][]=undefined&z[1][]=null", @"{""z"":[[""9"",""true""],[""undefined"",""null""]]}"), InlineData("a[0][x]=2", @"{""a"":[{""x"":""2""}]}"), InlineData("a%5B%5D=2", @"{""a"":[""2""]}"), InlineData("a%5B%5D=", @"{""a"":[""""]}"), InlineData("z%5B%5D=9&z%5B%5D=true&z%5B%5D=undefined&z%5B%5D=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z%5B%5D=9&z%5B%5D=true&z%5B%5D=undefined&z%5B%5D=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z%5B0%5D%5B%5D=9&z%5B0%5D%5B%5D=true&z%5B1%5D%5B%5D=undefined&z%5B1%5D%5B%5D=null", @"{""z"":[[""9"",""true""],[""undefined"",""null""]]}")] public void TestArray(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data originated from JS arrays, using the jQuery 1.3 format (no []'s). /// [Theory, InlineData("z=9&z=true&z=undefined&z=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z=9&z=true&z=undefined&z=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z=9&z=true&z=undefined&z=null&a=hello", @"{""z"":[""9"",""true"",""undefined"",""null""],""a"":""hello""}")] public void TestArrayCompat(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded data originated from JS arrays. /// [Theory, InlineData("a[z]=2&a[z]=3")] public void TestArrayCompatNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for form-urlencoded data originated from sparse JS arrays. /// [Theory, InlineData("a[2]=hello", @"{""a"":{""2"":""hello""}}"), InlineData("a[x][0]=2", @"{""a"":{""x"":[""2""]}}"), InlineData("a[x][1]=2", @"{""a"":{""x"":{""1"":""2""}}}"), InlineData("a[x][0]=0&a[x][1]=1", @"{""a"":{""x"":[""0"",""1""]}}"), InlineData("a[0][0][0]=hello&a[1][0][0][0][]=hello", @"{""a"":[[[""hello""]],[[[[""hello""]]]]]}"), InlineData("a[0][0][0]=hello&a[1][0][0][0]=hello", @"{""a"":[[[""hello""]],[[[""hello""]]]]}"), InlineData("a[1][0][]=1", @"{""a"":{""1"":[[""1""]]}}"), InlineData("a[1][1][]=1", @"{""a"":{""1"":{""1"":[""1""]}}}"), InlineData("a[1][1][0]=1", @"{""a"":{""1"":{""1"":[""1""]}}}"), InlineData("a[0][]=2&a[0][]=3&a[2][]=1", "{\"a\":{\"0\":[\"2\",\"3\"],\"2\":[\"1\"]}}"), InlineData("a[x][]=1&a[x][1]=2", @"{""a"":{""x"":[""1"",""2""]}}"), InlineData("a[x][0]=1&a[x][]=2", @"{""a"":{""x"":[""1"",""2""]}}")] public void TestArraySparse(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded arrays. /// [Theory, InlineData("a[x]=2&a[x][]=3"), InlineData("a[]=1&a[0][]=2"), InlineData("a[]=1&a[0][0][]=2"), InlineData("a[x][]=1&a[x][0]=2"), InlineData("a[][]=0"), InlineData("a[][x]=0")] public void TestArrayIndexNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing complex object graphs form-urlencoded. /// [Theory] [PropertyData("TestObjectTestData")] public void TestObject(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data with encoded names. /// [Theory] [PropertyData("TestEncodedNameTestData")] public void TestEncodedName(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for malformed form-urlencoded data. /// [Theory, InlineData("a[b=2"), InlineData("a[[b]=2"), InlineData("a[b]]=2")] public void TestNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } private static void BuildParams(string prefix, JToken jsonValue, List results) { if (jsonValue is JValue) { JValue jsonPrimitive = jsonValue as JValue; if (jsonPrimitive != null) { if (jsonPrimitive.Type == JTokenType.String && String.IsNullOrEmpty(jsonPrimitive.Value.ToString())) { results.Add(prefix + "=" + String.Empty); } else { if (jsonPrimitive.Value is DateTime || jsonPrimitive.Value is DateTimeOffset) { string dateStr = jsonPrimitive.ToString(); if (!String.IsNullOrEmpty(dateStr) && dateStr.StartsWith("\"")) { dateStr = dateStr.Substring(1, dateStr.Length - 2); } results.Add(prefix + "=" + WebUtility.UrlEncode(dateStr)); } else { results.Add(prefix + "=" + WebUtility.UrlEncode(jsonPrimitive.Value.ToString())); } } } else { results.Add(prefix + "=" + String.Empty); } } else if (jsonValue is JArray) { for (int i = 0; i < ((JArray)jsonValue).Count; i++) { if (jsonValue[i] is JArray || jsonValue[i] is JObject) { BuildParams(prefix + "[" + i + "]", jsonValue[i], results); } else { BuildParams(prefix + "[]", jsonValue[i], results); } } } else { //jsonValue is JObject foreach (KeyValuePair item in (JObject)jsonValue) { BuildParams(prefix + "[" + item.Key + "]", item.Value, results); } } } private static void ParseInvalidFormUrlEncoded(string encoded) { byte[] data = Encoding.UTF8.GetBytes(encoded); for (var cnt = 1; cnt <= data.Length; cnt++) { ICollection> collection; FormUrlEncodedParser parser = FormUrlEncodedParserTests.CreateParser(data.Length + 1, out collection); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = FormUrlEncodedParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.ThrowsArgument(() => { FormUrlEncodedJson.Parse(collection); }, null); } } private static void ValidateFormUrlEncoded(string encoded, string expectedResult) { byte[] data = Encoding.UTF8.GetBytes(encoded); for (var cnt = 1; cnt <= data.Length; cnt++) { ICollection> collection; FormUrlEncodedParser parser = FormUrlEncodedParserTests.CreateParser(data.Length + 1, out collection); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = FormUrlEncodedParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); JObject result = FormUrlEncodedJson.Parse(collection); Assert.NotNull(result); Assert.Equal(expectedResult, result.ToString(Newtonsoft.Json.Formatting.None)); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedFromUriQueryTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Http; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class FormUrlEncodedJsonFromUriQueryTests { [Theory, InlineData("abc", "{\"abc\":\"\"}"), InlineData("%2eabc%2e", "{\".abc.\":\"\"}"), InlineData("", "{}"), InlineData("a=1", "{\"a\":\"1\"}")] public void SimpleStringsTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a=2", "{\"a\":\"2\"}"), InlineData("b=true", "{\"b\":\"true\"}"), InlineData("c=hello", "{\"c\":\"hello\"}"), InlineData("d=", "{\"d\":\"\"}"), InlineData("e=null", "{\"e\":\"null\"}")] public void SimpleObjectsTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Fact] public void LegacyArraysTest() { ValidateFormUrlEncoded("a=1&a=hello&a=333", "{\"a\":[\"1\",\"hello\",\"333\"]}"); // Only valid in shallow serialization ParseInvalidFormUrlEncoded("a[z]=2&a[z]=3"); } [Theory, InlineData("a[]=1&a[]=hello&a[]=333", "{\"a\":[\"1\",\"hello\",\"333\"]}"), InlineData("a[b][]=1&a[b][]=hello&a[b][]=333", "{\"a\":{\"b\":[\"1\",\"hello\",\"333\"]}}"), InlineData("a[]=", "{\"a\":[\"\"]}"), InlineData("a%5B%5D=2", @"{""a"":[""2""]}"), InlineData("a[x][0]=1&a[x][]=2", @"{""a"":{""x"":[""1"",""2""]}}")] public void ArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[0][]=1&a[0][]=hello&a[1][]=333", "{\"a\":[[\"1\",\"hello\"],[\"333\"]]}"), InlineData("a[b][0][]=1&a[b][1][]=hello&a[b][1][]=333", "{\"a\":{\"b\":[[\"1\"],[\"hello\",\"333\"]]}}"), InlineData("a[0][0][0][]=1", "{\"a\":[[[[\"1\"]]]]}")] public void MultidimensionalArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[0][]=hello&a[2][]=333", "{\"a\":{\"0\":[\"hello\"],\"2\":[\"333\"]}}"), InlineData("a[0]=hello", "{\"a\":[\"hello\"]}"), InlineData("a[1][]=hello", "{\"a\":{\"1\":[\"hello\"]}}"), InlineData("a[1][0]=hello", "{\"a\":{\"1\":[\"hello\"]}}")] public void SparseArraysTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("b[]=2&b[1][c]=d", "{\"b\":[\"2\",{\"c\":\"d\"}]}")] public void ArraysWithMixedMembers(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("=3", "{\"\":\"3\"}"), InlineData("a=1&=3", "{\"a\":\"1\",\"\":\"3\"}"), InlineData("=3&b=2", "{\"\":\"3\",\"b\":\"2\"}")] public void EmptyKeyTest(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } [Theory, InlineData("a[b]=1&a=2"), InlineData("a[b]=1&a[b][]=2"), InlineData("a[x][]=1&a[x][0]=2"), InlineData("a=2&a[b]=1"), InlineData("[]=1"), InlineData("a[][]=0"), InlineData("a[][x]=0"), InlineData("a&a[b]=1")] public void InvalidObjectGraphsTest(string encoded) { ParseInvalidFormUrlEncoded(encoded); } [Theory, InlineData("a[b=2"), InlineData("a[[b]=2"), InlineData("a[b]]=2")] public void InvalidFormUrlEncodingTest(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing form-urlencoded data originated from JS primitives. /// [Theory, InlineData("abc", @"{""abc"":""""}"), InlineData("123", @"{""123"":""""}"), InlineData("true", @"{""true"":""""}"), InlineData("", "{}"), InlineData("%2fabc%2f", @"{""/abc/"":""""}")] public void TestJValue(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded data originated from JS primitives. /// [Theory, InlineData("a[b]=1&a=2"), InlineData("a=2&a[b]=1"), InlineData("[]=1")] public void TestJValueNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing form-urlencoded data originated from JS objects. /// [Theory, InlineData("a=NaN", @"{""a"":""NaN""}"), InlineData("a=false", @"{""a"":""false""}"), InlineData("a=foo", @"{""a"":""foo""}"), InlineData("1=1", "{\"1\":\"1\"}")] public void TestObjects(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data originated from JS arrays. /// [Theory, InlineData("a[]=2", @"{""a"":[""2""]}"), InlineData("a[]=", @"{""a"":[""""]}"), InlineData("a[0][0][]=1", @"{""a"":[[[""1""]]]}"), InlineData("z[]=9&z[]=true&z[]=undefined&z[]=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z[]=9&z[]=true&z[]=undefined&z[]=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z[0][]=9&z[0][]=true&z[1][]=undefined&z[1][]=null", @"{""z"":[[""9"",""true""],[""undefined"",""null""]]}"), InlineData("a[0][x]=2", @"{""a"":[{""x"":""2""}]}"), InlineData("a%5B%5D=2", @"{""a"":[""2""]}"), InlineData("a%5B%5D=", @"{""a"":[""""]}"), InlineData("z%5B%5D=9&z%5B%5D=true&z%5B%5D=undefined&z%5B%5D=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z%5B%5D=9&z%5B%5D=true&z%5B%5D=undefined&z%5B%5D=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z%5B0%5D%5B%5D=9&z%5B0%5D%5B%5D=true&z%5B1%5D%5B%5D=undefined&z%5B1%5D%5B%5D=null", @"{""z"":[[""9"",""true""],[""undefined"",""null""]]}")] public void TestArray(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data originated from JS arrays, using the jQuery 1.3 format (no []'s). /// [Theory, InlineData("z=9&z=true&z=undefined&z=", @"{""z"":[""9"",""true"",""undefined"",""""]}"), InlineData("z=9&z=true&z=undefined&z=null", @"{""z"":[""9"",""true"",""undefined"",""null""]}"), InlineData("z=9&z=true&z=undefined&z=null&a=hello", @"{""z"":[""9"",""true"",""undefined"",""null""],""a"":""hello""}")] public void TestArrayCompat(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded data originated from JS arrays. /// [Theory, InlineData("a[z]=2&a[z]=3")] public void TestArrayCompatNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for form-urlencoded data originated from sparse JS arrays. /// [Theory, InlineData("a[2]=hello", @"{""a"":{""2"":""hello""}}"), InlineData("a[x][0]=2", @"{""a"":{""x"":[""2""]}}"), InlineData("a[x][1]=2", @"{""a"":{""x"":{""1"":""2""}}}"), InlineData("a[x][0]=0&a[x][1]=1", @"{""a"":{""x"":[""0"",""1""]}}"), InlineData("a[0][0][0]=hello&a[1][0][0][0][]=hello", @"{""a"":[[[""hello""]],[[[[""hello""]]]]]}"), InlineData("a[0][0][0]=hello&a[1][0][0][0]=hello", @"{""a"":[[[""hello""]],[[[""hello""]]]]}"), InlineData("a[1][0][]=1", @"{""a"":{""1"":[[""1""]]}}"), InlineData("a[1][1][]=1", @"{""a"":{""1"":{""1"":[""1""]}}}"), InlineData("a[1][1][0]=1", @"{""a"":{""1"":{""1"":[""1""]}}}"), InlineData("a[0][]=2&a[0][]=3&a[2][]=1", "{\"a\":{\"0\":[\"2\",\"3\"],\"2\":[\"1\"]}}"), InlineData("a[x][]=1&a[x][1]=2", @"{""a"":{""x"":[""1"",""2""]}}"), InlineData("a[x][0]=1&a[x][]=2", @"{""a"":{""x"":[""1"",""2""]}}")] public void TestArraySparse(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Negative tests for parsing form-urlencoded arrays. /// [Theory, InlineData("a[x]=2&a[x][]=3"), InlineData("a[]=1&a[0][]=2"), InlineData("a[]=1&a[0][0][]=2"), InlineData("a[x][]=1&a[x][0]=2"), InlineData("a[][]=0"), InlineData("a[][x]=0")] public void TestArrayIndexNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } /// /// Tests for parsing complex object graphs form-urlencoded. /// [Theory] [TestDataSet(typeof(FormUrlEncodedJsonFromContentTests), "TestObjectTestData")] public void TestObject(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for parsing form-urlencoded data with encoded names. /// [Theory] [TestDataSet(typeof(FormUrlEncodedJsonFromContentTests), "TestEncodedNameTestData")] public void TestEncodedName(string encoded, string expectedResult) { ValidateFormUrlEncoded(encoded, expectedResult); } /// /// Tests for malformed form-urlencoded data. /// [Theory, InlineData("a[b=2"), InlineData("a[[b]=2"), InlineData("a[b]]=2")] public void TestNegative(string encoded) { ParseInvalidFormUrlEncoded(encoded); } private static void BuildParams(string prefix, JToken jsonValue, List results) { if (jsonValue is JValue) { JValue jsonPrimitive = jsonValue as JValue; if (jsonPrimitive != null) { if (jsonPrimitive.Type == JTokenType.String && String.IsNullOrEmpty(jsonPrimitive.Value.ToString())) { results.Add(prefix + "=" + String.Empty); } else { if (jsonPrimitive.Value is DateTime || jsonPrimitive.Value is DateTimeOffset) { string dateStr = jsonPrimitive.ToString(); if (!String.IsNullOrEmpty(dateStr) && dateStr.StartsWith("\"")) { dateStr = dateStr.Substring(1, dateStr.Length - 2); } results.Add(prefix + "=" + WebUtility.UrlEncode(dateStr)); } else { results.Add(prefix + "=" + WebUtility.UrlEncode(jsonPrimitive.Value.ToString())); } } } else { results.Add(prefix + "=" + String.Empty); } } else if (jsonValue is JArray) { for (int i = 0; i < ((JArray)jsonValue).Count; i++) { if (jsonValue[i] is JArray || jsonValue[i] is JObject) { BuildParams(prefix + "[" + i + "]", jsonValue[i], results); } else { BuildParams(prefix + "[]", jsonValue[i], results); } } } else { //jsonValue is JObject foreach (KeyValuePair item in (JObject)jsonValue) { BuildParams(prefix + "[" + item.Key + "]", item.Value, results); } } } private static Uri GetQueryUri(string query) { UriBuilder uriBuilder = new UriBuilder("http://some.host"); uriBuilder.Query = query; return uriBuilder.Uri; } private static void ParseInvalidFormUrlEncoded(string encoded) { Uri address = GetQueryUri(encoded); JObject result; Assert.False(address.TryReadQueryAsJson(out result), "Expected parsing to return false"); Assert.Null(result); } private static void ValidateFormUrlEncoded(string encoded, string expectedResult) { Uri address = GetQueryUri(encoded); JObject result; Assert.True(address.TryReadQueryAsJson(out result), "Expected parsing to return true"); Assert.NotNull(result); Assert.Equal(expectedResult, result.ToString(Newtonsoft.Json.Formatting.None)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedJsonTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class FormUrlEncodedJsonTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(FormUrlEncodedJson), TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsStatic); } [Fact] public void ParseThrowsOnNull() { Assert.ThrowsArgumentNull(() => FormUrlEncodedJson.Parse(null), null); } [Fact] public void ParseThrowsInvalidMaxDepth() { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => FormUrlEncodedJson.Parse(CreateQuery(), -1), "maxDepth", "1", -1); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => FormUrlEncodedJson.Parse(CreateQuery(), 0), "maxDepth", "1", 0); } [Fact] public void ParseThrowsMaxDepthExceeded() { // Depth of 'a[b]=1' is 3 IEnumerable> query = CreateQuery(new KeyValuePair("a[b]", "1")); Assert.ThrowsArgument(() => { FormUrlEncodedJson.Parse(query, 2); }, null); // This should succeed Assert.NotNull(FormUrlEncodedJson.Parse(query, 3)); } [Fact] public void TryParseThrowsOnNull() { JObject value; Assert.ThrowsArgumentNull(() => FormUrlEncodedJson.TryParse(null, out value), null); } [Fact] public void TryParseThrowsInvalidMaxDepth() { JObject value; Assert.ThrowsArgumentGreaterThanOrEqualTo(() => FormUrlEncodedJson.TryParse(CreateQuery(), -1, out value), "maxDepth", "1", -1); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => FormUrlEncodedJson.TryParse(CreateQuery(), 0, out value), "maxDepth", "1", 0); } [Fact] public void TryParseReturnsFalseMaxDepthExceeded() { JObject value; // Depth of 'a[b]=1' is 3 IEnumerable> query = CreateQuery(new KeyValuePair("a[b]", "1")); Assert.False(FormUrlEncodedJson.TryParse(query, 2, out value), "Parse should have failed due to too high depth."); // This should succeed Assert.True(FormUrlEncodedJson.TryParse(query, 3, out value), "Expected non-null JsonObject instance"); Assert.NotNull(value); } private static IEnumerable> CreateQuery(params KeyValuePair[] namevaluepairs) { return new List>(namevaluepairs); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/FormUrlEncodedMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class FormUrlEncodedMediaTypeFormatterTests { private const int MinBufferSize = 256; private const int DefaultBufferSize = 32 * 1024; private const int DefaultMaxDepth = 1024; [Fact] void CopyConstructor() { TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter() { MaxDepth = 42, ReadBufferSize = 512 }; TestFormUrlEncodedMediaTypeFormatter derivedFormatter = new TestFormUrlEncodedMediaTypeFormatter(formatter); Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth); Assert.Equal(formatter.ReadBufferSize, derivedFormatter.ReadBufferSize); } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(FormUrlEncodedMediaTypeFormatter), TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void SupportedMediaTypes_HeaderValuesAreNotSharedBetweenInstances() { var formatter1 = new FormUrlEncodedMediaTypeFormatter(); var formatter2 = new FormUrlEncodedMediaTypeFormatter(); foreach (MediaTypeHeaderValue mediaType1 in formatter1.SupportedMediaTypes) { MediaTypeHeaderValue mediaType2 = formatter2.SupportedMediaTypes.Single(m => m.Equals(mediaType1)); Assert.NotSame(mediaType1, mediaType2); } } [Fact] public void SupportEncodings_ValuesAreNotSharedBetweenInstances() { var formatter1 = new FormUrlEncodedMediaTypeFormatter(); var formatter2 = new FormUrlEncodedMediaTypeFormatter(); foreach (Encoding encoding1 in formatter1.SupportedEncodings) { Encoding encoding2 = formatter2.SupportedEncodings.Single(e => e.Equals(encoding1)); Assert.NotSame(encoding1, encoding2); } } [Theory] [TestDataSet(typeof(HttpTestData), "StandardFormUrlEncodedMediaTypes")] public void Constructor(MediaTypeHeaderValue mediaType) { FormUrlEncodedMediaTypeFormatter formatter = new FormUrlEncodedMediaTypeFormatter(); Assert.True(formatter.SupportedMediaTypes.Contains(mediaType), String.Format("SupportedMediaTypes should have included {0}.", mediaType.ToString())); } [Fact] public void DefaultMediaTypeReturnsApplicationJson() { MediaTypeHeaderValue mediaType = FormUrlEncodedMediaTypeFormatter.DefaultMediaType; Assert.NotNull(mediaType); Assert.Equal("application/x-www-form-urlencoded", mediaType.MediaType); } [Fact] public void ReadBufferSize_RoundTrips() { Assert.Reflection.IntegerProperty( new FormUrlEncodedMediaTypeFormatter(), c => c.ReadBufferSize, expectedDefaultValue: 32 * 1024, minLegalValue: 256, illegalLowerValue: 255, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 1024); } [Fact] public void MaxDepthReturnsCorrectValue() { Assert.Reflection.IntegerProperty( new FormUrlEncodedMediaTypeFormatter(), f => f.MaxDepth, expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 10); } [Fact] public async Task ReadDeeplyNestedObjectThrows() { FormUrlEncodedMediaTypeFormatter formatter = new FormUrlEncodedMediaTypeFormatter() { MaxDepth = 100 }; StringContent content = new StringContent(GetDeeplyNestedObject(125)); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var contentStream = await content.ReadAsStreamAsync(); await Assert.ThrowsAsync( () => formatter.ReadFromStreamAsync(typeof(JToken), contentStream, content, null)); } [Fact] public async Task ReadDeeplyNestedObjectWithBigDepthQuotaWorks() { FormUrlEncodedMediaTypeFormatter formatter = new FormUrlEncodedMediaTypeFormatter() { MaxDepth = 150 }; StringContent content = new StringContent(GetDeeplyNestedObject(125)); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var contentStream = await content.ReadAsStreamAsync(); JToken result = (JToken)(await formatter.ReadFromStreamAsync(typeof(JToken), contentStream, content, null)); Assert.NotNull(result); } static string GetDeeplyNestedObject(int depth) { StringBuilder sb = new StringBuilder("a"); for (int i = 0; i < depth; i++) { sb.Append("[a]"); } sb.Append("=1"); return sb.ToString(); } [Fact] public void CanReadTypeThrowsOnNull() { TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.CanReadType(null); }, "type"); } [Theory] [InlineData(typeof(FormDataCollection))] [InlineData(typeof(JToken))] public void CanReadTypeTrue(Type type) { TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.True(formatter.CanReadType(type)); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanReadTypeReturnsFalse(Type variationType, object testData) { GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.False(formatter.CanReadType(variationType)); // Ask a 2nd time to probe whether the cached result is treated the same Assert.False(formatter.CanReadType(variationType)); } [Fact] public void CanWriteTypeThrowsOnNull() { TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.CanWriteType(null); }, "type"); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanWriteTypeReturnsFalse(Type variationType, object testData) { GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.False(formatter.CanWriteType(variationType), "formatter should have returned false."); // Ask a 2nd time to probe whether the cached result is treated the same Assert.False(formatter.CanWriteType(variationType), "formatter should have returned false on 2nd try as well."); } [Fact] public void ReadFromStreamThrowsOnNull() { TestFormUrlEncodedMediaTypeFormatter formatter = new TestFormUrlEncodedMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.ReadFromStreamAsync(null, Stream.Null, null, null); }, "type"); Assert.ThrowsArgumentNull(() => { formatter.ReadFromStreamAsync(typeof(object), null, null, null); }, "readStream"); } [Fact] public void WriteToStreamAsyncThrowsNotImplemented() { FormUrlEncodedMediaTypeFormatter formatter = new FormUrlEncodedMediaTypeFormatter(); Assert.Throws( () => formatter.WriteToStreamAsync(typeof(object), new object(), Stream.Null, null, null), "The media type formatter of type 'FormUrlEncodedMediaTypeFormatter' does not support writing because it does not implement the WriteToStreamAsync method."); } public class TestFormUrlEncodedMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter { public TestFormUrlEncodedMediaTypeFormatter() { } public TestFormUrlEncodedMediaTypeFormatter(TestFormUrlEncodedMediaTypeFormatter formatter) : base(formatter) { } public new bool CanReadType(Type type) { return base.CanReadType(type); } public new bool CanWriteType(Type type) { return base.CanWriteType(type); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/JsonMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Formatting.DataSets.Types; using System.Net.Http.Headers; using System.Runtime.Serialization.Json; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class JsonMediaTypeFormatterTests : MediaTypeFormatterTestBase { // Test data which should round-trip without type information in the serialization. Contains an exhaustive // selection of JSON native types. (BSON also supports Int32, Int64, DateTime, Guid, ... natively.) private static readonly RefTypeTestData BunchOfJsonObjectsTestData = new RefTypeTestData( () => new List { null, String.Empty, "This is a string", false, true, Double.MinValue, Double.MaxValue, }); // Test data for DBNull. Separate from BunchOfJsonObjectsTestData because DBNull will round-trip as null. private static readonly RefTypeTestData DBNullAsObjectTestData = new RefTypeTestData( () => new List { DBNull.Value, }); public static IEnumerable BunchOfJsonObjectsTestDataCollection { get { return new TestData[] { BunchOfJsonObjectsTestData, }; } } public static IEnumerable DBNullAsObjectTestDataCollection { get { return new TestData[] { DBNullAsObjectTestData, }; } } public static List JTokenTypes { get { return new List { typeof(JToken), typeof(JValue), typeof(JArray), typeof(JObject) }; } } public static IEnumerable ValueAndRefTypeTestDataCollectionExceptULong { get { // Include neither ISerializable data set nor unsigned longs return CommonUnitTestDataSets.ValueAndRefTypeTestDataCollection.Except( new TestData[] { CommonUnitTestDataSets.Ulongs, CommonUnitTestDataSets.ISerializableTypes }); } } public override IEnumerable ExpectedSupportedMediaTypes { get { return HttpTestData.StandardJsonMediaTypes; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return ExpectedSupportedEncodings.ElementAt(0).GetBytes("{\"Number\":42}"); } } [Fact] void CopyConstructor() { TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter() { Indent = true, MaxDepth = 42, UseDataContractJsonSerializer = true }; TestJsonMediaTypeFormatter derivedFormatter = new TestJsonMediaTypeFormatter(formatter); Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth); Assert.Equal(formatter.UseDataContractJsonSerializer, derivedFormatter.UseDataContractJsonSerializer); Assert.Equal(formatter.Indent, derivedFormatter.Indent); Assert.Same(formatter.SerializerSettings, derivedFormatter.SerializerSettings); Assert.Same(formatter.SerializerSettings.ContractResolver, derivedFormatter.SerializerSettings.ContractResolver); } [Fact] public void DefaultMediaType_ReturnsApplicationJson() { MediaTypeHeaderValue mediaType = JsonMediaTypeFormatter.DefaultMediaType; Assert.NotNull(mediaType); Assert.Equal("application/json", mediaType.MediaType); } [Fact] public void Indent_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlMediaTypeFormatter(), c => c.Indent, expectedDefaultValue: false); } [Fact] public void MaxDepth_RoundTrips() { Assert.Reflection.IntegerProperty( new JsonMediaTypeFormatter(), c => c.MaxDepth, expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 256); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanReadType_ReturnsExpectedValues(Type variationType, object testData) { TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); bool isSerializable = IsTypeSerializableWithJsonSerializer(variationType, testData); bool canSupport = formatter.CanReadTypeProxy(variationType); // If we don't agree, we assert only if the DCJ serializer says it cannot support something we think it should Assert.False(isSerializable != canSupport && isSerializable, String.Format("CanReadType returned wrong value for '{0}'.", variationType)); // Ask a 2nd time to probe whether the cached result is treated the same canSupport = formatter.CanReadTypeProxy(variationType); Assert.False(isSerializable != canSupport && isSerializable, String.Format("2nd CanReadType returned wrong value for '{0}'.", variationType)); } [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateFails() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "The 'CreateJsonSerializer' method threw an exception when attempting to create a JSON serializer."); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerJsonSerializer); } [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateReturnsNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "The 'CreateJsonSerializer' method returned null. It must return a JSON serializer instance."); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerJsonSerializer); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateFails() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "The 'CreateJsonSerializer' method threw an exception when attempting to create a JSON serializer."); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerJsonSerializer); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateReturnsNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "The 'CreateJsonSerializer' method returned null. It must return a JSON serializer instance."); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerJsonSerializer); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public async Task DataContractFormatterThrowsOnWriteWhenOverridenCreateFails() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; formatter.UseDataContractJsonSerializer = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "The 'DataContractJsonSerializer' serializer cannot serialize the type 'SampleType'."); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerJsonSerializer); } [Fact] public async Task DataContractFormatterThrowsOnWriteWhenOverridenCreateReturnsNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; formatter.UseDataContractJsonSerializer = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action, "The 'DataContractJsonSerializer' serializer cannot serialize the type 'SampleType'."); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerJsonSerializer); } [Fact] public async Task DataContractFormatterThrowsOnReadWhenOverridenCreateFails() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; formatter.UseDataContractJsonSerializer = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "The 'DataContractJsonSerializer' serializer cannot serialize the type 'SampleType'."); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerJsonSerializer); } [Fact] public async Task DataContractFormatterThrowsOnReadWhenOverridenCreateReturnsNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; formatter.UseDataContractJsonSerializer = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action, "The 'DataContractJsonSerializer' serializer cannot serialize the type 'SampleType'."); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerJsonSerializer); } #endif [Fact] public void CanReadType_ReturnsTrueOnJtoken() { TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); foreach (Type type in JTokenTypes) { Assert.True(formatter.CanReadTypeProxy(type), "formatter should have returned true."); } } [Fact] public void CanWriteType_ReturnsTrueOnJtoken() { TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); foreach (Type type in JTokenTypes) { Assert.True(formatter.CanWriteTypeProxy(type), "formatter should have returned false."); } } [Fact] public async Task ReadFromStreamAsync_RoundTripsJToken() { string beforeMessage = "Hello World"; TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); JToken before = beforeMessage; MemoryStream memStream = new MemoryStream(); JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(memStream)); before.WriteTo(jsonWriter); jsonWriter.Flush(); memStream.Position = 0; JToken after = (await Assert.Task.SucceedsWithResultAsync(formatter.ReadFromStreamAsync(typeof(JToken), memStream, null, null))) as JToken; Assert.NotNull(after); string afterMessage = after.ToObject(); Assert.Equal(beforeMessage, afterMessage); } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "ValueAndRefTypeTestDataCollectionExceptULong", RoundTripDataVariations)] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "BunchOfJsonObjectsTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync(Type variationType, object testData) { // Guard bool canSerialize = IsTypeSerializableWithJsonSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } #if !NETCOREAPP2_1 // DBNull not serializable on .NET Core 2.1. // Test alternate null value; always serialized as "null" [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AllSingleInstances)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull(Type variationType, object testData) { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Assert.Null(readObj); } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsDictionary)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Dictionary(Type variationType, object testData) { // Guard IDictionary expectedDictionary = Assert.IsType>(testData); // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into collections. Assert.Equal(testData.GetType(), readObj.GetType()); IDictionary readDictionary = (IDictionary)readObj; Assert.Equal(expectedDictionary.Count, readDictionary.Count); foreach (string key in expectedDictionary.Keys) { Assert.True(readDictionary.ContainsKey(key)); Assert.Null(readDictionary[key]); } } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsArray | TestDataVariations.AsList)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Enumerable(Type variationType, object testData) { // Guard Assert.True((testData as IEnumerable) != null); // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); IEnumerable expectedEnumerable = (IEnumerable)testData; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into collections. Assert.Equal(testData.GetType(), readObj.GetType()); IEnumerable readEnumerable = (IEnumerable)readObj; Assert.Equal(expectedEnumerable.Count(), readEnumerable.Count()); foreach (object readContent in readEnumerable) { Assert.Null(readContent); } } [Theory] [TestDataSet(typeof(JsonMediaTypeFormatterTests), "DBNullAsObjectTestDataCollection", TestDataVariations.AsClassMember)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNull_Holder(Type variationType, object testData) { // Guard Assert.IsType>(testData); // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value can be read back as null object. Reach into objects. Assert.Equal(testData.GetType(), readObj.GetType()); TestDataHolder readDataHolder = (TestDataHolder)readObj; Assert.Null(readDataHolder.V1); } [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNullAsNullString() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); Type variationType = typeof(string); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.value can be read as null of any nullable type Assert.Null(readObj); } [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_DBNull() { // Arrange TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); Type variationType = typeof(DBNull); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // Only JSON case where DBNull.Value round-trips Assert.Equal(testData, readObj); } #endif [Fact] public async Task UseDataContractJsonSerializer_False() { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter { UseDataContractJsonSerializer = false }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); //Assert.True(serializedString.Contains("DataContractSampleType"), // "SampleType should be serialized with data contract name DataContractSampleType because UseDataContractJsonSerializer is set to true."); Assert.False(serializedString.Contains("\r\n"), "Using JsonSerializer should emit data without indentation by default."); } [Fact] public async Task UseDataContractJsonSerializer_False_Indent() { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter { UseDataContractJsonSerializer = false, Indent = true }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("\r\n"), "Using JsonSerializer with Indent set to true should emit data with indentation."); } [Theory] [InlineData(typeof(IQueryable))] [InlineData(typeof(IEnumerable))] public async Task UseJsonFormatterWithNull(Type type) { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter { UseDataContractJsonSerializer = false }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(type, null, memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("null"), "Using Json formatter to serialize null should emit 'null'."); } [Fact] public async Task WriteToStreamAsync_RoundTripsJToken() { string beforeMessage = "Hello World"; TestJsonMediaTypeFormatter formatter = new TestJsonMediaTypeFormatter(); JToken before = new JValue(beforeMessage); MemoryStream memStream = new MemoryStream(); await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(typeof(JToken), before, memStream, null, null)); memStream.Position = 0; JToken after = JToken.Load(new JsonTextReader(new StreamReader(memStream))); string afterMessage = after.ToObject(); Assert.Equal(beforeMessage, afterMessage); } public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); string formattedContent = "\"" + content + "\""; string mediaType = string.Format("application/json; charset={0}", encoding); // Act & assert return ReadContentUsingCorrectCharacterEncodingHelperAsync( formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); string formattedContent = "\"" + content + "\""; string mediaType = string.Format("application/json; charset={0}", encoding); // Act & assert return WriteContentUsingCorrectCharacterEncodingHelperAsync( formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } #if NET6_0_OR_GREATER // Cannot Mock a Stream and let JsonWriter write to it. Writer will use ReadOnlySpan in this case and such // parameters are not currently mockable. See moq/moq4#829, moq/moq4#979, and dotnet/runtime#45152. // Override here avoids the Mock and should confirm this Stream is not closed. Also adds an // additional check of the written text. [Fact] public override async Task WriteToStreamAsync_WhenObjectIsNull_WritesDataButDoesNotCloseStream() { // Arrange JsonMediaTypeFormatter formatter = CreateFormatter(); Stream stream = new MemoryStream(); HttpContent content = new StreamContent(Stream.Null); // Act await formatter.WriteToStreamAsync(typeof(SampleType), null, stream, content, null); // Assert (stream will throw if it has been closed) stream.Position = 0; using var reader = new StreamReader(stream); Assert.Equal("null", reader.ReadToEnd()); } #endif public class TestJsonMediaTypeFormatter : JsonMediaTypeFormatter { public TestJsonMediaTypeFormatter() { } public TestJsonMediaTypeFormatter(TestJsonMediaTypeFormatter formatter) : base(formatter) { } public bool ThrowAnExceptionOnCreate { get; set; } public bool ReturnNullOnCreate { get; set; } public JsonSerializer InnerJsonSerializer { get; private set; } public DataContractJsonSerializer InnerDataContractSerializer { get; private set; } public bool CanReadTypeProxy(Type type) { return CanReadType(type); } public bool CanWriteTypeProxy(Type type) { return CanWriteType(type); } public override JsonSerializer CreateJsonSerializer() { InnerJsonSerializer = base.CreateJsonSerializer(); if (ReturnNullOnCreate) { return null; } if (ThrowAnExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return InnerJsonSerializer; } public override DataContractJsonSerializer CreateDataContractSerializer(Type type) { InnerDataContractSerializer = base.CreateDataContractSerializer(type); if (ReturnNullOnCreate) { return null; } if (ThrowAnExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return InnerDataContractSerializer; } } private bool IsTypeSerializableWithJsonSerializer(Type type, object obj) { try { new DataContractJsonSerializer(type); if (obj != null && obj.GetType() != type) { new DataContractJsonSerializer(obj.GetType()); } } catch { return false; } return !Assert.Http.IsKnownUnserializable(type, obj, (t) => typeof(INotJsonSerializable).IsAssignableFrom(t)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/JsonNetSerializationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Portions copyright (c) 2007 James Newton-King // // 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. using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class JsonNetSerializationTest { public static TheoryDataSet SerializedJson { get { return new TheoryDataSet() { // Primitives { 'f', "\"f\"" }, { "abc", "\"abc\"" }, { "\"\\", @"""\""\\""" }, { 256, "256" }, { (ulong)long.MaxValue, long.MaxValue.ToString() }, { 45.78m, "45.78" }, { .00000457823432, "4.57823432E-06" }, { (byte)24, "24" }, { false, "false" }, { AttributeTargets.Assembly | AttributeTargets.Constructor, "33" }, { ConsoleColor.DarkCyan, "3" }, { new DateTimeOffset(1999, 5, 27, 4, 34, 45, TimeSpan.Zero), "\"1999-05-27T04:34:45+00:00\"" }, { new TimeSpan(5, 30, 0), "\"05:30:00\"" }, { new Uri("http://www.bing.com"), @"""http://www.bing.com""" }, { new Uri("http://www.bing.com/"), @"""http://www.bing.com/""" }, { new Uri("http://www.bing.com/foo"), @"""http://www.bing.com/foo""" }, { new Uri("http://www.bing.com/foo/"), @"""http://www.bing.com/foo/""" }, { new Guid("4ed1cd44-11d7-4b27-b623-0b8b553c8906"), "\"4ed1cd44-11d7-4b27-b623-0b8b553c8906\"" }, // Structs { new Point() { x = 45, Y = -5}, "{\"x\":45,\"Y\":-5}" }, // Arrays { new object[] {}, "[]" }, { new int[] { 1, 2, 3}, "[1,2,3]" }, { new string[] { "a", "b"}, "[\"a\",\"b\"]" }, { new Point[] { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" }, // Collections { new List { 1, 2, 3}, "[1,2,3]" }, { new List { "a", "b"}, "[\"a\",\"b\"]" }, { new List { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" }, { new MyList { 1, 2, 3}, "[1,2,3]" }, { new MyList { "a", "b"}, "[\"a\",\"b\"]" }, { new MyList { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" }, // Dictionaries { new Dictionary { { "k1", "v1" }, { "k2", "v2" } }, "{\"k1\":\"v1\",\"k2\":\"v2\"}" }, { new Dictionary { { 1, "v1" }, { 2, "v2" } }, "{\"1\":\"v1\",\"2\":\"v2\"}" }, // Anonymous types { new { Anon1 = 56, Anon2 = "foo"}, "{\"Anon1\":56,\"Anon2\":\"foo\"}" }, // Classes { new DataContractType() { s = "foo", i = 49, NotAMember = "Error" }, "{\"s\":\"foo\",\"i\":49}" }, { new POCOType() { s = "foo", t = "Error"}, "{\"s\":\"foo\"}" }, #if !Testing_NetStandard1_3 // Only publics are serialized in netstandard1.3 { new SerializableType("protected") { publicField = "public", protectedInternalField = "protected internal", internalField = "internal", PublicProperty = "private", nonSerializedField = "Error" }, "{\"publicField\":\"public\",\"internalField\":\"internal\",\"protectedInternalField\":\"protected internal\",\"protectedField\":\"protected\",\"privateField\":\"private\"}" }, #else { new SerializableType("protected") { publicField = "public", protectedInternalField = "protected internal", internalField = "internal", PublicProperty = "private", nonSerializedField = "Error" }, "{\"publicField\":\"public\",\"PublicProperty\":\"private\"}" }, #endif { new { field1 = "x", field2 = (string)null, field3 = "y" }, "{\"field1\":\"x\",\"field2\":null,\"field3\":\"y\"}" }, // Generics { new KeyValuePair("foo", false), "{\"Key\":\"foo\",\"Value\":false}" }, // ISerializable types { new ISerializableType() { Property = "Value" }, "{\"SomeProperty\":\"Value\"}" }, // JSON Values { new JValue(false), "false" }, { new JValue(54), "54" }, { new JValue("s"), "\"s\"" }, { new JArray() { new JValue(1), new JValue(2) }, "[1,2]" }, { new JObject() { { "k1", new JValue("v1") }, { "k2", new JValue("v2") } }, "{\"k1\":\"v1\",\"k2\":\"v2\"}" }, { new KeyValuePair(new JValue("k"), new JArray() { new JValue("v1"), new JValue("v2") }), "{\"Key\":\"k\",\"Value\":[\"v1\",\"v2\"]}" }, }; } } public static TheoryDataSet TypedSerializedJson { get { return new TheoryDataSet() { // Null { null, "null", typeof(POCOType) }, { JValue.CreateNull(), "null", typeof(JToken) }, // Nullables { new int?(), "null", typeof(int?) }, { new Point?(), "null", typeof(Point?) }, { new ConsoleColor?(), "null", typeof(ConsoleColor?) }, { new int?(45), "45", typeof(int?) }, { new Point?(new Point() { x = 45, Y = -5 }), "{\"x\":45,\"Y\":-5}", typeof(Point?) }, { new ConsoleColor?(ConsoleColor.DarkMagenta), "5", typeof(ConsoleColor?)}, }; } } [Theory] [PropertyData("SerializedJson")] public Task ObjectsSerializeToExpectedJson(object o, string expectedJson) { return ObjectsSerializeToExpectedJsonWithProvidedType(o, expectedJson, o.GetType()); } [Theory] [PropertyData("SerializedJson")] public Task JsonDeserializesToExpectedObject(object expectedObject, string json) { return JsonDeserializesToExpectedObjectWithProvidedType(expectedObject, json, expectedObject.GetType()); } [Theory] [PropertyData("TypedSerializedJson")] public async Task ObjectsSerializeToExpectedJsonWithProvidedType(object o, string expectedJson, Type type) { Assert.Equal(expectedJson, await SerializeAsync(o, type)); } [Theory] [PropertyData("TypedSerializedJson")] public async Task JsonDeserializesToExpectedObjectWithProvidedType(object expectedObject, string json, Type type) { if (expectedObject == null) { Assert.Null(await DeserializeAsync(json, type)); } else { object o = await DeserializeAsync(json, type); Assert.Equal(expectedObject, o, new ObjectComparer()); } } [Fact] public async Task CallbacksGetCalled() { TypeWithCallbacks o = new TypeWithCallbacks(); string json = await SerializeAsync(o, typeof(TypeWithCallbacks)); Assert.Equal("12", o.callbackOrder); TypeWithCallbacks deserializedObject = (await DeserializeAsync(json, typeof(TypeWithCallbacks))) as TypeWithCallbacks; Assert.Equal("34", deserializedObject.callbackOrder); } [Fact] public async Task DerivedTypesArePreserved() { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); formatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects; string json = await SerializeAsync(new Derived(), typeof(Base), formatter); object deserializedObject = await DeserializeAsync(json, typeof(Base), formatter); Assert.IsType(deserializedObject); } [Fact] public async Task ArbitraryTypesArentDeserializedByDefault() { string json = "{\"$type\":\"" + typeof(DangerousType).AssemblyQualifiedName + "\"}"; object deserializedObject = await DeserializeAsync(json, typeof(object)); Assert.IsNotType(deserializedObject); } [Fact] public async Task ReferencesArePreserved() { Ref ref1 = new Ref(); Ref ref2 = new Ref(); ref1.Reference = ref2; ref2.Reference = ref1; string json = await SerializeAsync(ref1, typeof(Ref)); Ref deserializedObject = (await DeserializeAsync(json, typeof(Ref))) as Ref; Assert.ReferenceEquals(deserializedObject, deserializedObject.Reference.Reference); } [Fact] public async Task MissingMemberGetsDeserializedToNull() { string json = "{}"; POCOType deserializedObject = (await DeserializeAsync(json, typeof(POCOType))) as POCOType; Assert.Null(deserializedObject.s); } [Fact] public Task DeserializingDeepArraysThrows() { StringBuilder sb = new StringBuilder(); int depth = 500; for (int i = 0; i < depth; i++) { sb.Append("["); } sb.Append("null"); for (int i = 0; i < depth; i++) { sb.Append("]"); } string json = sb.ToString(); return Assert.ThrowsAsync(() => DeserializeAsync(json, typeof(object))); } [Theory] // existing good surrogate pair [InlineData("ABC \\ud800\\udc00 DEF", "ABC \ud800\udc00 DEF")] // invalid surrogates (two high back-to-back) [InlineData("ABC \\ud800\\ud800 DEF", "ABC \ufffd\ufffd DEF")] // invalid high surrogate at end of string [InlineData("ABC \\ud800", "ABC \ufffd")] // high surrogate not followed by low surrogate [InlineData("ABC \\ud800 DEF", "ABC \ufffd DEF")] // low surrogate not preceded by high surrogate [InlineData("ABC \\udc00\\ud800 DEF", "ABC \ufffd\ufffd DEF")] // make sure unencoded invalid surrogate characters don't make it through #if NETCOREAPP // Json.NET uses its regular invalid Unicode character on .NET Core 2.1; '?' elsewhere. [InlineData("\udc00\ud800\ud800", "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd")] #else [InlineData("\udc00\ud800\ud800", "??????")] #endif public async Task InvalidUnicodeStringsAreFixedUp(string input, string expectedString) { string json = "\"" + input + "\""; string deserializedString = (await DeserializeAsync(json, typeof(string))) as string; Assert.Equal(expectedString, deserializedString); } private static async Task SerializeAsync(object o, Type type, MediaTypeFormatter formatter = null) { formatter = formatter ?? new JsonMediaTypeFormatter(); MemoryStream ms = new MemoryStream(); await formatter.WriteToStreamAsync(type, o, ms, null, null); ms.Flush(); ms.Position = 0; return new StreamReader(ms).ReadToEnd(); } internal static Task DeserializeAsync(string json, Type type, MediaTypeFormatter formatter = null, IFormatterLogger formatterLogger = null) { formatter = formatter ?? new JsonMediaTypeFormatter(); MemoryStream ms = new MemoryStream(); byte[] bytes = Encoding.Default.GetBytes(json); ms.Write(bytes, 0, bytes.Length); ms.Flush(); ms.Position = 0; return formatter.ReadFromStreamAsync(type, ms, content: null, formatterLogger: formatterLogger); } } public class ObjectComparer : IEqualityComparer { bool IEqualityComparer.Equals(object x, object y) { Type xType = x.GetType(); if (xType == y.GetType()) { if (typeof(JToken).IsAssignableFrom(xType) || xType == typeof(ArgumentNullException) || xType == typeof(KeyValuePair)) { return x.ToString() == y.ToString(); } if (xType == typeof(DataContractType)) { return Equals(x, y); } if (xType == typeof(POCOType)) { return Equals(x, y); } if (xType == typeof(SerializableType)) { return Equals(x, y); } if (xType == typeof(ISerializableType)) { return Equals(x, y); } if (xType == typeof(Point)) { return Equals(x, y); } if (typeof(IEnumerable).IsAssignableFrom(xType)) { IEnumerator xEnumerator = ((IEnumerable)x).GetEnumerator(); IEnumerator yEnumerator = ((IEnumerable)y).GetEnumerator(); while (xEnumerator.MoveNext()) { // if x is longer than y if (!yEnumerator.MoveNext()) { return false; } else { if (!xEnumerator.Current.Equals(yEnumerator.Current)) { return false; } } } // if y is longer than x if (yEnumerator.MoveNext()) { return false; } return true; } } return x.Equals(y); } int IEqualityComparer.GetHashCode(object obj) { throw new NotImplementedException(); } bool Equals(object x, object y) where T : IEquatable { IEquatable yEquatable = (IEquatable)y; return yEquatable.Equals((T)x); } } // Marked as [Serializable] to check that [DataContract] takes precedence over [Serializable] [DataContract] [Serializable] public class DataContractType : IEquatable { [DataMember] public string s; [DataMember] internal int i; public string NotAMember; public bool Equals(DataContractType other) { return this.s == other.s && this.i == other.i; } } public class POCOType : IEquatable { public string s; internal string t; public bool Equals(POCOType other) { return this.s == other.s; } } public class MyList : ICollection { List innerList = new List(); public IEnumerator GetEnumerator() { return innerList.GetEnumerator(); } public void Add(T item) { innerList.Add(item); } public void Clear() { innerList.Clear(); } public bool Contains(T item) { return innerList.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { innerList.CopyTo(array, arrayIndex); } public int Count { get { return innerList.Count; } } public bool IsReadOnly { get { return ((ICollection)innerList).IsReadOnly; } } public bool Remove(T item) { return innerList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return innerList.GetEnumerator(); } } [Serializable] class SerializableType : IEquatable { [JsonConstructor] public SerializableType(string protectedFieldValue) { this.protectedField = protectedFieldValue; } public string publicField; internal string internalField; protected internal string protectedInternalField; protected string protectedField; private string privateField; public string PublicProperty { get { return privateField; } set { this.privateField = value; } } [NonSerialized] public string nonSerializedField; public bool Equals(SerializableType other) { #if !Testing_NetStandard1_3 // Only publics are serialized in netstandard1.3. privateField is serialized through PublicProperty return this.publicField == other.publicField && this.internalField == other.internalField && this.protectedInternalField == other.protectedInternalField && this.protectedField == other.protectedField && this.privateField == other.privateField; #else // this.privateField is serialized through this.PublicProperty, thus the comparison here return this.publicField == other.publicField && this.privateField == other.privateField; #endif } } public struct Point : IEquatable { public int x; public int Y { get; set; } public bool Equals(Point other) { return this.x == other.x && this.Y == other.Y; } } [DataContract(IsReference = true)] public class Ref { [DataMember] public Ref Reference; } public class Base { } public class Derived : Base { } [DataContract] public class TypeWithCallbacks { public string callbackOrder = ""; [OnSerializing] public void OnSerializing(StreamingContext c) { callbackOrder += "1"; } [OnSerialized] public void OnSerialized(StreamingContext c) { callbackOrder += "2"; } [OnDeserializing] public void OnDeserializing(StreamingContext c) { callbackOrder += "3"; } [OnDeserialized] public void OnDeserialized(StreamingContext c) { callbackOrder += "4"; } } public class DangerousType { } [Serializable] public class ISerializableType : ISerializable, IEquatable { public ISerializableType() { } protected ISerializableType(SerializationInfo info, StreamingContext context) { // The key here for GetValue/SetValue is intentionally different from the property name. // This tests that the serializer is using the result of GetObjectData, rather than the property // name. Property = (string)info.GetValue("SomeProperty", typeof(String)); } public string Property { get; set; } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("SomeProperty", this.Property, typeof(String)); } public bool Equals(ISerializableType other) { return String.Equals(Property, other.Property, StringComparison.Ordinal); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/JsonNetValidationTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Reflection; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Formatting { public class JsonNetValidationTest { public static TheoryDataSet Theories { get { return new TheoryDataSet() { // Type coercion {"null", typeof(int), 1}, {"45", typeof(string), 0}, {"random text", typeof(DateTimeOffset), 1}, {"[1,2,3]", typeof(string[]), 0}, {"\"foo\"", typeof(int), 1}, {"\"foo\"", typeof(DateTime), 1}, {"[\"a\",\"b\",\"45\",34]", typeof(int[]), 2}, {"[\"a\",\"b\",\"45\",34]", typeof(DateTime[]), 5}, // Required members {"{}", typeof(DataContractWithRequiredMembers), 2}, {"[{},{},{}]", typeof(DataContractWithRequiredMembers[]), 6}, // Throwing setters {"{\"Throws\":\"foo\"}", typeof(TypeWithThrowingSetter), 1}, {"[{\"Throws\":\"foo\"},{\"Throws\":\"foo\"}]", typeof(TypeWithThrowingSetter[]), 2}, }; } } [Theory] [PropertyData("Theories")] public async Task ModelErrorsPopulatedWithValidationErrors(string json, Type type, int expectedErrors) { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); formatter.RequiredMemberSelector = new SimpleRequiredMemberSelector(); Mock mockLogger = new Mock() { }; await JsonNetSerializationTest.DeserializeAsync(json, type, formatter, mockLogger.Object); mockLogger.Verify(mock => mock.LogError(It.IsAny(), It.IsAny()), Times.Exactly(expectedErrors)); } [Fact] public async Task HittingMaxDepthRaisesOnlyOneValidationError() { // Arrange JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); Mock mockLogger = new Mock(); StringBuilder sb = new StringBuilder("{'A':null}"); for (int i = 0; i < 5000; i++) { sb.Insert(0, "{'A':"); sb.Append('}'); } string json = sb.ToString(); // Act await JsonNetSerializationTest.DeserializeAsync(json, typeof(Nest), formatter, mockLogger.Object); // Assert mockLogger.Verify(mock => mock.LogError(It.IsAny(), It.IsAny()), Times.Once()); } } // this IRMS treats all member names that start with "Required" as required public class SimpleRequiredMemberSelector : IRequiredMemberSelector { public bool IsRequiredMember(MemberInfo member) { return member.Name.StartsWith("Required"); } } public class DataContractWithRequiredMembers { public string Required1; public string Required2; public string Optional; } public class TypeWithThrowingSetter { public string Throws { get { return "foo"; } set { throw new NotImplementedException(); } } } public class Nest { public Nest A { get; set; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeConstantsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class MediaTypeConstantsTests { private static void ValidateClones(MediaTypeHeaderValue clone1, MediaTypeHeaderValue clone2, string charset) { Assert.NotNull(clone1); Assert.NotNull(clone2); Assert.NotSame(clone1, clone2); Assert.Equal(clone1.MediaType, clone2.MediaType); Assert.Equal(charset, clone1.CharSet); Assert.Equal(charset, clone2.CharSet); } [Fact] public void ApplicationOctetStreamMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.ApplicationOctetStreamMediaType, MediaTypeConstants.ApplicationOctetStreamMediaType, null); } [Fact] public void ApplicationXmlMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.ApplicationXmlMediaType, MediaTypeConstants.ApplicationXmlMediaType, null); } [Fact] public void ApplicationJsonMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.ApplicationJsonMediaType, MediaTypeConstants.ApplicationJsonMediaType, null); } [Fact] public void TextXmlMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.TextXmlMediaType, MediaTypeConstants.TextXmlMediaType, null); } [Fact] public void TextJsonMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.TextJsonMediaType, MediaTypeConstants.TextJsonMediaType, null); } [Fact] public void ApplicationFormUrlEncodedMediaType_ReturnsClone() { ValidateClones(MediaTypeConstants.ApplicationFormUrlEncodedMediaType, MediaTypeConstants.ApplicationFormUrlEncodedMediaType, null); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeFormatterCollectionTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Web.Http; using System.Xml; using System.Xml.Linq; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http.Formatting { public class MediaTypeFormatterCollectionTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(MediaTypeFormatterCollection), TypeAssert.TypeProperties.IsPublicVisibleClass, typeof(Collection)); } [Fact] public void Constructor() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); Assert.Equal(3, collection.Count); Assert.NotNull(collection.XmlFormatter); Assert.NotNull(collection.JsonFormatter); Assert.NotNull(collection.FormUrlEncodedFormatter); } [Fact] public void Constructor1_AcceptsEmptyList() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[0]); Assert.Empty(collection); } [Theory] [TestDataSet(typeof(HttpTestData), "AllFormatterCollections")] public void Constructor1_SetsProperties(IEnumerable formatterCollection) { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatterCollection); if (collection.OfType().Any()) { Assert.NotNull(collection.XmlFormatter); } else { Assert.Null(collection.XmlFormatter); } if (collection.OfType().Any()) { Assert.NotNull(collection.JsonFormatter); } else { Assert.Null(collection.JsonFormatter); } } [Fact] public void Constructor1_SetsDerivedFormatters() { // force to array to get stable instances MediaTypeFormatter[] derivedFormatters = HttpTestData.DerivedFormatters.ToArray(); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(derivedFormatters); Assert.True(derivedFormatters.SequenceEqual(collection)); } [Fact] public void Constructor1_ThrowsWithNullFormatters() { Assert.ThrowsArgumentNull(() => new MediaTypeFormatterCollection(null), "formatters"); } [Fact] public void Constructor1_ThrowsWithNullFormatterInCollection() { Assert.ThrowsArgument( () => new MediaTypeFormatterCollection(new MediaTypeFormatter[] { null }), "formatters", Error.Format(Properties.Resources.CannotHaveNullInList, typeof(MediaTypeFormatter).Name)); } [Fact] public void Constructor1_AcceptsDuplicateFormatterTypes() { MediaTypeFormatter[] formatters = new MediaTypeFormatter[] { new XmlMediaTypeFormatter(), new JsonMediaTypeFormatter(), new FormUrlEncodedMediaTypeFormatter(), new XmlMediaTypeFormatter(), new JsonMediaTypeFormatter(), new FormUrlEncodedMediaTypeFormatter(), }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatters); Assert.True(formatters.SequenceEqual(collection)); } [Fact] public void MediaTypeFormatterCollection_Changing_FiresOnClear() { TestChanging((collection) => collection.Clear(), 1); } [Fact] public void MediaTypeFormatterCollection_Changing_FiresOnInsert() { TestChanging((collection) => collection.Insert(0, new XmlMediaTypeFormatter()), 1); } [Fact] public void MediaTypeFormatterCollection_Changing_FiresOnRemove() { TestChanging((collection) => collection.RemoveAt(0), 1); } [Fact] public void MediaTypeFormatterCollection_Changing_FiresOnSet() { TestChanging((collection) => collection[0] = new XmlMediaTypeFormatter(), 1); } private static void TestChanging(Action mutation, int expectedCount) { // Arrange MediaTypeFormatter formatter1 = new XmlMediaTypeFormatter(); MediaTypeFormatter formatter2 = new JsonMediaTypeFormatter(); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { formatter1, formatter2 }); int changeCount = 0; collection.Changing += (source, args) => { changeCount++; }; // Act mutation(collection); //Assert Assert.Equal(expectedCount, changeCount); } [Fact] public void XmlFormatter_SetByCtor() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { formatter }); Assert.Same(formatter, collection.XmlFormatter); } [Fact] public void XmlFormatter_ClearedByCtor() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[0]); Assert.Null(collection.XmlFormatter); } [Fact] public void JsonFormatter_SetByCtor() { JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { formatter }); Assert.Same(formatter, collection.JsonFormatter); } [Fact] public void JsonFormatter_ClearedByCtor() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[0]); Assert.Null(collection.JsonFormatter); } [Fact] public void FormUrlEncodedFormatter_SetByCtor() { FormUrlEncodedMediaTypeFormatter formatter = new FormUrlEncodedMediaTypeFormatter(); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { formatter }); Assert.Same(formatter, collection.FormUrlEncodedFormatter); } [Fact] public void FormUrlEncodedFormatter_ClearedByCtor() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(new MediaTypeFormatter[0]); Assert.Null(collection.FormUrlEncodedFormatter); } [Fact] public void Remove_SetsXmlFormatter() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); int count = collection.Count; collection.Remove(collection.XmlFormatter); Assert.Null(collection.XmlFormatter); Assert.Equal(count - 1, collection.Count); } [Fact] public void Remove_SetsJsonFormatter() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); int count = collection.Count; collection.Remove(collection.JsonFormatter); Assert.Null(collection.JsonFormatter); Assert.Equal(count - 1, collection.Count); } [Fact] public void Insert_SetsXmlFormatter() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); int count = collection.Count; XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); collection.Insert(0, formatter); Assert.Same(formatter, collection.XmlFormatter); Assert.Equal(count + 1, collection.Count); } [Fact] public void Insert_SetsJsonFormatter() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); int count = collection.Count; JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); collection.Insert(0, formatter); Assert.Same(formatter, collection.JsonFormatter); Assert.Equal(count + 1, collection.Count); } [Fact] public void FindReader_ThrowsOnNullType() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("text/test"); Assert.ThrowsArgumentNull(() => collection.FindReader(type: null, mediaType: mediaType), "type"); } [Fact] public void FindReader_ThrowsOnNullMediaType() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); Assert.ThrowsArgumentNull(() => collection.FindReader(type: typeof(object), mediaType: null), "mediaType"); } [Fact] public void FindReader_ReturnsNullOnNoMatch() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); collection.Clear(); collection.Add(formatter); MediaTypeHeaderValue contentType = new MediaTypeHeaderValue("text/test"); // Act MediaTypeFormatter actualFormatter = collection.FindReader(typeof(object), contentType); // Assert Assert.Null(actualFormatter); } [Theory] [TestDataSet( typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", typeof(HttpTestData), "LegalMediaTypeStrings")] public void FindReader_ReturnsFormatterOnMatch(Type variationType, object testData, string mediaType) { // Arrange GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true }; foreach (string legalMediaType in HttpTestData.LegalMediaTypeStrings) { formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue(legalMediaType)); } MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); collection.Clear(); collection.Add(formatter); MediaTypeHeaderValue contentType = new MediaTypeHeaderValue(mediaType); // Act MediaTypeFormatter actualFormatter = collection.FindReader(variationType, contentType); // Assert Assert.Same(formatter, actualFormatter); } [Fact] public void FindWriter_ThrowsOnNullType() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("text/test"); Assert.ThrowsArgumentNull(() => collection.FindWriter(type: null, mediaType: mediaType), "type"); } [Fact] public void FindWriter_ThrowsOnNullMediaType() { MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); Assert.ThrowsArgumentNull(() => collection.FindWriter(type: typeof(object), mediaType: null), "mediaType"); } [Fact] public void FindWriter_ReturnsNullOnNoMatch() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); collection.Clear(); collection.Add(formatter); MediaTypeHeaderValue contentType = new MediaTypeHeaderValue("text/test"); // Act MediaTypeFormatter actualFormatter = collection.FindWriter(typeof(object), contentType); // Assert Assert.Null(actualFormatter); } [Theory] [TestDataSet( typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", typeof(HttpTestData), "LegalMediaTypeStrings")] public void FindWriter_ReturnsFormatterOnMatch(Type variationType, object testData, string mediaType) { // Arrange GC.KeepAlive(testData); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true }; foreach (string legalMediaType in HttpTestData.LegalMediaTypeStrings) { formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue(mediaType)); } MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(); collection.Clear(); collection.Add(formatter); MediaTypeHeaderValue contentType = new MediaTypeHeaderValue(mediaType); // Act MediaTypeFormatter actualFormatter = collection.FindWriter(variationType, contentType); // Assert Assert.Same(formatter, actualFormatter); } [Theory] [InlineData(typeof(JObject))] [InlineData(typeof(XAttribute))] [InlineData(typeof(Type))] [InlineData(typeof(byte[]))] [InlineData(typeof(XmlElement))] [InlineData(typeof(FormDataCollection))] public void IsTypeExcludedFromValidation_ReturnsTrueForExcludedTypes(Type type) { Assert.True(MediaTypeFormatterCollection.IsTypeExcludedFromValidation(type)); } [Fact] public void WritingFormatters_FiltersOutCanWriteAnyTypesFalse() { // Arrange MockMediaTypeFormatter writableFormatter = new MockMediaTypeFormatter(); MockMediaTypeFormatter readOnlyFormatter = new MockMediaTypeFormatter() { CanWriteAnyTypesReturn = false }; List formatters = new List() { writableFormatter, readOnlyFormatter }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatters); // Act MediaTypeFormatter[] writableFormatters = collection.WritingFormatters; // Assert MediaTypeFormatter[] expectedFormatters = new MediaTypeFormatter[] { writableFormatter }; Assert.Equal(expectedFormatters, writableFormatters); } [Fact] public void WritingFormatters_FiltersOutNull() { // Arrange MockMediaTypeFormatter writableFormatter = new MockMediaTypeFormatter(); List formatters = new List() { writableFormatter }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatters); collection.Add(null); // Act MediaTypeFormatter[] writableFormatters = collection.WritingFormatters; // Assert MediaTypeFormatter[] expectedFormatters = new MediaTypeFormatter[] { writableFormatter }; Assert.Equal(expectedFormatters, writableFormatters); } [Fact] public void WritingFormatters_Caches() { // Arrange MockMediaTypeFormatter formatter1 = new MockMediaTypeFormatter(); MockMediaTypeFormatter formatter2 = new MockMediaTypeFormatter(); List formatters = new List() { formatter1, formatter2 }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatters); // Act MediaTypeFormatter[] writableFormatters1 = collection.WritingFormatters; MediaTypeFormatter[] writableFormatters2 = collection.WritingFormatters; // Assert MediaTypeFormatter[] expectedFormatters = formatters.ToArray(); Assert.Equal(expectedFormatters, writableFormatters1); Assert.Same(writableFormatters1, writableFormatters2); } [Fact] public void WritingFormatters_Insert_ResetsCache() { TestWritingFormattersCacheReset((collection) => collection.Insert(0, new MockMediaTypeFormatter())); } [Fact] public void WritingFormatters_RemoveAt_ResetsCache() { TestWritingFormattersCacheReset((collection) => collection.RemoveAt(0)); } private static void TestWritingFormattersCacheReset(Action mutation) { // Arrange MockMediaTypeFormatter formatter1 = new MockMediaTypeFormatter(); MockMediaTypeFormatter formatter2 = new MockMediaTypeFormatter(); List formatters = new List() { formatter1, formatter2 }; MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(formatters); // Act mutation(collection); MediaTypeFormatter[] expectedFormatters = collection.ToArray(); MediaTypeFormatter[] writableFormatters = collection.WritingFormatters; // Assert Assert.Equal(expectedFormatters, writableFormatters); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeFormatterExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class MediaTypeFormatterExtensionsTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(MediaTypeFormatterExtensions), TypeAssert.TypeProperties.IsPublicVisibleClass | TypeAssert.TypeProperties.IsStatic); } [Fact] public void AddQueryStringMappingThrowsWithNullThis() { MediaTypeFormatter formatter = null; Assert.ThrowsArgumentNull(() => formatter.AddQueryStringMapping("name", "value", new MediaTypeHeaderValue("application/xml")), "formatter"); } [Fact] public void AddQueryStringMapping1ThrowsWithNullThis() { MediaTypeFormatter formatter = null; Assert.ThrowsArgumentNull(() => formatter.AddQueryStringMapping("name", "value", "application/xml"), "formatter"); } [Fact] public void AddRequestHeaderMappingThrowsWithNullThis() { MediaTypeFormatter formatter = null; Assert.ThrowsArgumentNull(() => formatter.AddRequestHeaderMapping("name", "value", StringComparison.CurrentCulture, true, new MediaTypeHeaderValue("application/xml")), "formatter"); } [Fact] public void AddRequestHeaderMappingAddsSuccessfully() { MediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.Empty(formatter.MediaTypeMappings); formatter.AddRequestHeaderMapping("name", "value", StringComparison.CurrentCulture, true, new MediaTypeHeaderValue("application/xml")); IEnumerable mappings = formatter.MediaTypeMappings.OfType(); RequestHeaderMapping mapping = Assert.Single(mappings); Assert.Equal("name", mapping.HeaderName); Assert.Equal("value", mapping.HeaderValue); Assert.Equal(StringComparison.CurrentCulture, mapping.HeaderValueComparison); Assert.True(mapping.IsValueSubstring); Assert.Equal(new MediaTypeHeaderValue("application/xml"), mapping.MediaType); } [Fact] public void AddRequestHeaderMapping1ThrowsWithNullThis() { MediaTypeFormatter formatter = null; Assert.ThrowsArgumentNull(() => formatter.AddRequestHeaderMapping("name", "value", StringComparison.CurrentCulture, true, "application/xml"), "formatter"); } [Fact] public void AddRequestHeaderMapping1AddsSuccessfully() { MediaTypeFormatter formatter = new MockMediaTypeFormatter(); Assert.Empty(formatter.MediaTypeMappings); formatter.AddRequestHeaderMapping("name", "value", StringComparison.CurrentCulture, true, "application/xml"); IEnumerable mappings = formatter.MediaTypeMappings.OfType(); RequestHeaderMapping mapping = Assert.Single(mappings); Assert.Equal("name", mapping.HeaderName); Assert.Equal("value", mapping.HeaderValue); Assert.Equal(StringComparison.CurrentCulture, mapping.HeaderValueComparison); Assert.True(mapping.IsValueSubstring); Assert.Equal(new MediaTypeHeaderValue("application/xml"), mapping.MediaType); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeFormatterMatchTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class MediaTypeFormatterMatchTest { [Fact] public void Ctor_ThrowsOnNullFormatter() { Assert.ThrowsArgumentNull(() => new MediaTypeFormatterMatch(null, null, null, MediaTypeFormatterMatchRanking.None), "formatter"); } [Fact] public void Ctor_ClonesMediaType() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); MediaTypeHeaderValue mediaType = MediaTypeHeaderValue.Parse("text/test"); // Act MediaTypeFormatterMatch match = new MediaTypeFormatterMatch(formatter, mediaType, null, MediaTypeFormatterMatchRanking.MatchOnCanWriteType); // Assert Assert.Equal(mediaType, match.MediaType); Assert.NotSame(mediaType, match.MediaType); } [Fact] public void Ctor_InitializesDefaultValues() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); // Act MediaTypeFormatterMatch match = new MediaTypeFormatterMatch(formatter, null, null, MediaTypeFormatterMatchRanking.MatchOnCanWriteType); // Assert Assert.Same(formatter, match.Formatter); Assert.Equal(MediaTypeConstants.ApplicationOctetStreamMediaType, match.MediaType); Assert.Equal(FormattingUtilities.Match, match.Quality); Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnCanWriteType, match.Ranking); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeFormatterTestBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.Serialization; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Formatting { /// /// A test class for common functionality across multiple implementations. /// /// The type of formatter under test. public abstract class MediaTypeFormatterTestBase where TFormatter : MediaTypeFormatter { protected MediaTypeFormatterTestBase() { } // Test data variations of interest in round-trip tests. public const TestDataVariations RoundTripDataVariations = TestDataVariations.All | TestDataVariations.WithNull | TestDataVariations.AsClassMember; public abstract IEnumerable ExpectedSupportedMediaTypes { get; } public abstract IEnumerable ExpectedSupportedEncodings { get; } /// /// Byte representation of an with value 42 using the default encoding /// for this media type formatter. /// public abstract byte[] ExpectedSampleTypeByteRepresentation { get; } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void SupportedMediaTypes_HeaderValuesAreNotSharedBetweenInstances() { var formatter1 = CreateFormatter(); var formatter2 = CreateFormatter(); foreach (MediaTypeHeaderValue mediaType1 in formatter1.SupportedMediaTypes) { MediaTypeHeaderValue mediaType2 = formatter2.SupportedMediaTypes.Single(m => m.Equals(mediaType1)); Assert.NotSame(mediaType1, mediaType2); } } [Fact] public void SupportEncodings_ValuesAreNotSharedBetweenInstances() { var formatter1 = CreateFormatter(); var formatter2 = CreateFormatter(); foreach (Encoding mediaType1 in formatter1.SupportedEncodings) { Encoding mediaType2 = formatter2.SupportedEncodings.Single(m => m.Equals(mediaType1)); Assert.NotSame(mediaType1, mediaType2); } } [Fact] public void SupportMediaTypes_DefaultSupportedMediaTypes() { TFormatter formatter = CreateFormatter(); Assert.True(ExpectedSupportedMediaTypes.SequenceEqual(formatter.SupportedMediaTypes)); } [Fact] public void SupportEncoding_DefaultSupportedEncodings() { TFormatter formatter = CreateFormatter(); Assert.True(ExpectedSupportedEncodings.SequenceEqual(formatter.SupportedEncodings)); } [Fact] public void ReadFromStreamAsync_ThrowsOnNull() { TFormatter formatter = CreateFormatter(); Assert.ThrowsArgumentNull(() => { formatter.ReadFromStreamAsync(null, Stream.Null, null, null); }, "type"); Assert.ThrowsArgumentNull(() => { formatter.ReadFromStreamAsync(typeof(object), null, null, null); }, "readStream"); } [Fact] public async Task ReadFromStreamAsync_WhenContentLengthIsZero_DoesNotReadStream() { // Arrange TFormatter formatter = CreateFormatter(); Mock mockStream = new Mock(); IFormatterLogger mockFormatterLogger = new Mock().Object; HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; contentHeaders.ContentLength = 0; // Act await formatter.ReadFromStreamAsync(typeof(SampleType), mockStream.Object, content, mockFormatterLogger); // Assert mockStream.Verify(s => s.Read(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); mockStream.Verify(s => s.ReadByte(), Times.Never()); mockStream.Verify(s => s.BeginRead(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); } [Fact] public async Task ReadFromStreamAsync_WhenContentLengthIsZero_DoesNotCloseStream() { // Arrange TFormatter formatter = CreateFormatter(); Mock mockStream = new Mock(); IFormatterLogger mockFormatterLogger = new Mock().Object; HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; contentHeaders.ContentLength = 0; // Act await formatter.ReadFromStreamAsync(typeof(SampleType), mockStream.Object, content, mockFormatterLogger); // Assert mockStream.Verify(s => s.Close(), Times.Never()); } [Theory] [InlineData(false)] [InlineData(0)] [InlineData("")] public async Task ReadFromStreamAsync_WhenContentLengthIsZero_ReturnsDefaultTypeValue(T value) { // Arrange GC.KeepAlive(value); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. TFormatter formatter = CreateFormatter(); HttpContent content = new StringContent(""); // Act var contentStream = await content.ReadAsStreamAsync(); var result = await formatter.ReadFromStreamAsync(typeof(T), contentStream, content, null); // Assert Assert.Equal(default(T), (T)result); } [Fact] public virtual async Task ReadFromStreamAsync_ReadsDataButDoesNotCloseStream() { // Arrange TFormatter formatter = CreateFormatter(); MemoryStream memStream = new MemoryStream(ExpectedSampleTypeByteRepresentation); HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; contentHeaders.ContentLength = memStream.Length; contentHeaders.ContentType = CreateSupportedMediaType(); // Act var result = await formatter.ReadFromStreamAsync(typeof(SampleType), memStream, content, null); // Assert Assert.True(memStream.CanRead); var value = Assert.IsType(result); Assert.Equal(42, value.Number); } [Fact] public virtual async Task ReadFromStreamAsync_WhenContentLengthIsNull_ReadsDataButDoesNotCloseStream() { // Arrange TFormatter formatter = CreateFormatter(); MemoryStream memStream = new MemoryStream(ExpectedSampleTypeByteRepresentation); HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; contentHeaders.ContentLength = null; contentHeaders.ContentType = CreateSupportedMediaType(); // Act var result = await formatter.ReadFromStreamAsync(typeof(SampleType), memStream, content, null); // Assert Assert.True(memStream.CanRead); var value = Assert.IsType(result); Assert.Equal(42, value.Number); } [Fact] public void WriteToStreamAsync_ThrowsOnNull() { TFormatter formatter = CreateFormatter(); Assert.ThrowsArgumentNull(() => { formatter.WriteToStreamAsync(null, new object(), Stream.Null, null, null); }, "type"); Assert.ThrowsArgumentNull(() => { formatter.WriteToStreamAsync(typeof(object), new object(), null, null, null); }, "writeStream"); } [Fact] public virtual async Task WriteToStreamAsync_WhenObjectIsNull_WritesDataButDoesNotCloseStream() { // Arrange TFormatter formatter = CreateFormatter(); Mock mockStream = new Mock(); mockStream.Setup(s => s.CanWrite).Returns(true); HttpContent content = new StringContent(String.Empty); // Act await formatter.WriteToStreamAsync(typeof(SampleType), null, mockStream.Object, content, null); // Assert mockStream.Verify(s => s.Close(), Times.Never()); mockStream.Verify(s => s.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); } [Fact] public virtual async Task WriteToStreamAsync_WritesDataButDoesNotCloseStream() { // Arrange TFormatter formatter = CreateFormatter(); SampleType sampleType = new SampleType { Number = 42 }; MemoryStream memStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = CreateSupportedMediaType(); // Act await formatter.WriteToStreamAsync(typeof(SampleType), sampleType, memStream, content, null); // Assert Assert.True(memStream.CanRead); byte[] actualSampleTypeByteRepresentation = memStream.ToArray(); Assert.NotEmpty(actualSampleTypeByteRepresentation); } [Fact] public virtual async Task Overridden_WriteToStreamAsyncWithoutCancellationToken_GetsCalled() { // Arrange Stream stream = new MemoryStream(); Mock formatter = CreateMockFormatter(); ObjectContent content = new ObjectContent(42, formatter.Object); formatter .Setup(f => f.WriteToStreamAsync(typeof(int), 42, stream, content, null /* transportContext */)) .Returns(TaskHelpers.Completed()) .Verifiable(); // Act await content.CopyToAsync(stream); // Assert formatter.Verify(); } [Fact] public virtual async Task Overridden_WriteToStreamAsyncWithCancellationToken_GetsCalled() { // Arrange Stream stream = new MemoryStream(); Mock formatter = CreateMockFormatter(); ObjectContent content = new ObjectContent(42, formatter.Object); formatter .Setup(f => f.WriteToStreamAsync(typeof(int), 42, stream, content, null /* transportContext */, CancellationToken.None)) .Returns(TaskHelpers.Completed()) .Verifiable(); // Act await content.CopyToAsync(stream); // Assert formatter.Verify(); } [Fact] public virtual async Task Overridden_ReadFromStreamAsyncWithoutCancellationToken_GetsCalled() { // Arrange Stream stream = new MemoryStream(); Mock formatter = CreateMockFormatter(); formatter.Object.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/test")); StringContent content = new StringContent(" ", Encoding.Default, "application/test"); CancellationTokenSource cts = new CancellationTokenSource(); formatter .Setup(f => f.ReadFromStreamAsync(typeof(string), It.IsAny(), content, null /*formatterLogger */)) .Returns(Task.FromResult(null)) .Verifiable(); // Act await content.ReadAsAsync(new[] { formatter.Object }, cts.Token); // Assert formatter.Verify(); } [Fact] public virtual async Task Overridden_ReadFromStreamAsyncWithCancellationToken_GetsCalled() { // Arrange Stream stream = new MemoryStream(); Mock formatter = CreateMockFormatter(); formatter.Object.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/test")); StringContent content = new StringContent(" ", Encoding.Default, "application/test"); CancellationTokenSource cts = new CancellationTokenSource(); formatter .Setup(f => f.ReadFromStreamAsync(typeof(string), It.IsAny(), content, null /*formatterLogger */, cts.Token)) .Returns(Task.FromResult(null)) .Verifiable(); // Act await content.ReadAsAsync(new[] { formatter.Object }, cts.Token); // Assert formatter.Verify(); } // Remove this suppression once we pick up an xunit.analyzers package containing the xnuit/xunit#1466 fix. #pragma warning disable xUnit1013 // Public method should be marked as test [Theory] [TestDataSet(typeof(HttpTestData), "ReadAndWriteCorrectCharacterEncoding")] public abstract Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding); [Theory] [TestDataSet(typeof(HttpTestData), "ReadAndWriteCorrectCharacterEncoding")] public abstract Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding); #pragma warning restore xUnit1013 // Public method should be marked as test protected virtual TFormatter CreateFormatter() { ConstructorInfo constructor = typeof(TFormatter).GetConstructor(Type.EmptyTypes); return (TFormatter)constructor.Invoke(null); } protected virtual Mock CreateMockFormatter() { return new Mock() { CallBase = true }; } protected virtual MediaTypeHeaderValue CreateSupportedMediaType() { return ExpectedSupportedMediaTypes.First(); } public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(MediaTypeFormatter formatter, Type variationType, object testData) { // Arrange HttpContent content = new StringContent(String.Empty); HttpContentHeaders contentHeaders = content.Headers; object readObj = null; // Act & Assert await Assert.Stream.WriteAndReadAsync( async stream => { await Assert.Task.SucceedsAsync(formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null)); contentHeaders.ContentLength = stream.Length; }, async stream => readObj = await Assert.Task.SucceedsWithResultAsync(formatter.ReadFromStreamAsync(variationType, stream, content, formatterLogger: null))); return readObj; } protected async Task ReadFromStreamAsync_UsesCorrectCharacterEncodingHelper(MediaTypeFormatter formatter, string content, string formattedContent, string mediaType, string encoding, bool isDefaultEncoding) { // Arrange Encoding enc = null; if (isDefaultEncoding) { enc = formatter.SupportedEncodings.First((e) => e.WebName.Equals(encoding, StringComparison.OrdinalIgnoreCase)); } else { enc = Encoding.GetEncoding(encoding); formatter.SupportedEncodings.Add(enc); } byte[] data = enc.GetBytes(formattedContent); MemoryStream memStream = new MemoryStream(data); StringContent dummyContent = new StringContent(string.Empty); HttpContentHeaders headers = dummyContent.Headers; headers.Clear(); headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); headers.ContentLength = data.Length; IFormatterLogger mockFormatterLogger = new Mock().Object; // Act string result = (await formatter.ReadFromStreamAsync(typeof(string), memStream, dummyContent, mockFormatterLogger)) as string; // Assert Assert.Equal(content, result); } protected async Task WriteToStreamAsync_UsesCorrectCharacterEncodingHelper(MediaTypeFormatter formatter, string content, string formattedContent, string mediaType, string encoding, bool isDefaultEncoding) { // Arrange Encoding enc = null; if (isDefaultEncoding) { enc = formatter.SupportedEncodings.First((e) => e.WebName.Equals(encoding, StringComparison.OrdinalIgnoreCase)); } else { enc = Encoding.GetEncoding(encoding); formatter.SupportedEncodings.Add(enc); } byte[] preamble = enc.GetPreamble(); byte[] data = enc.GetBytes(formattedContent); byte[] expectedData = new byte[preamble.Length + data.Length]; Buffer.BlockCopy(preamble, 0, expectedData, 0, preamble.Length); Buffer.BlockCopy(data, 0, expectedData, preamble.Length, data.Length); MemoryStream memStream = new MemoryStream(); HttpContent httpContent = new StringContent(String.Empty); HttpContentHeaders contentHeaders = httpContent.Headers; contentHeaders.Clear(); contentHeaders.ContentType = MediaTypeHeaderValue.Parse(mediaType); contentHeaders.ContentLength = expectedData.Length; IFormatterLogger mockFormatterLogger = new Mock().Object; // Act await formatter.WriteToStreamAsync(typeof(string), content, memStream, httpContent, null); // Assert byte[] actualData = memStream.ToArray(); Assert.Equal(expectedData, actualData); } public static Encoding CreateOrGetSupportedEncoding(MediaTypeFormatter formatter, string encoding, bool isDefaultEncoding) { Encoding enc = null; if (isDefaultEncoding) { enc = formatter.SupportedEncodings.First((e) => e.WebName.Equals(encoding, StringComparison.OrdinalIgnoreCase)); } else { enc = Encoding.GetEncoding(encoding); formatter.SupportedEncodings.Add(enc); } return enc; } protected static Task ReadContentUsingCorrectCharacterEncodingHelperAsync(MediaTypeFormatter formatter, string content, string formattedContent, string mediaType, string encoding, bool isDefaultEncoding) { // Arrange Encoding enc = CreateOrGetSupportedEncoding(formatter, encoding, isDefaultEncoding); byte[] sourceData = enc.GetBytes(formattedContent); // Further Arrange, Act & Assert return ReadContentUsingCorrectCharacterEncodingHelperAsync(formatter, content, sourceData, mediaType); } protected static async Task ReadContentUsingCorrectCharacterEncodingHelperAsync(MediaTypeFormatter formatter, string content, byte[] sourceData, string mediaType) { // Arrange MemoryStream memStream = new MemoryStream(sourceData); StringContent dummyContent = new StringContent(string.Empty); HttpContentHeaders headers = dummyContent.Headers; headers.Clear(); headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); headers.ContentLength = sourceData.Length; IFormatterLogger mockFormatterLogger = new Mock().Object; // Act var result = (await formatter.ReadFromStreamAsync(typeof(string), memStream, dummyContent, mockFormatterLogger)) as string; // Assert Assert.Equal(content, result); } protected static Task WriteContentUsingCorrectCharacterEncodingHelperAsync(MediaTypeFormatter formatter, string content, string formattedContent, string mediaType, string encoding, bool isDefaultEncoding) { // Arrange Encoding enc = CreateOrGetSupportedEncoding(formatter, encoding, isDefaultEncoding); byte[] preamble = enc.GetPreamble(); byte[] data = enc.GetBytes(formattedContent); byte[] expectedData = new byte[preamble.Length + data.Length]; Buffer.BlockCopy(preamble, 0, expectedData, 0, preamble.Length); Buffer.BlockCopy(data, 0, expectedData, preamble.Length, data.Length); // Further Arrange, Act & Assert return WriteContentUsingCorrectCharacterEncodingHelperAsync(formatter, content, expectedData, mediaType); } protected static async Task WriteContentUsingCorrectCharacterEncodingHelperAsync(MediaTypeFormatter formatter, string content, byte[] expectedData, string mediaType) { // Arrange MemoryStream memStream = new MemoryStream(); StringContent dummyContent = new StringContent(string.Empty); HttpContentHeaders headers = dummyContent.Headers; headers.Clear(); headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); headers.ContentLength = expectedData.Length; IFormatterLogger mockFormatterLogger = new Mock().Object; // Act await formatter.WriteToStreamAsync(typeof(string), content, memStream, dummyContent, null); // Assert byte[] actualData = memStream.ToArray(); Assert.Equal(expectedData, actualData); } } [DataContract(Name = "DataContractSampleType")] public class SampleType { [DataMember] public int Number { get; set; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Formatting { [Xunit.Collection("MediaTypeFormatterTests")] // Isolate MaxCollectionKeySize_RoundTrips. [Xunit.CollectionDefinition("MediaTypeFormatterTests", DisableParallelization = true)] public class MediaTypeFormatterTests { private const string TestMediaType = "text/test"; private MediaTypeHeaderValue TestMediaTypeHeader = new MediaTypeHeaderValue(TestMediaType); public static TheoryDataSet SelectCharacterEncodingTestData { get { // string bodyEncoding, string[] supportedEncodings, string expectedEncoding return new TheoryDataSet { { null, new string[] { "utf-8", "utf-16"}, "utf-8" }, { null, new string[] { "utf-16", "utf-8"}, "utf-16"}, { "utf-32", new string[] { "utf-8", "utf-16"}, "utf-8" }, { "utf-32", new string[] { "utf-8", "utf-16", "utf-32"}, "utf-32"} }; } } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(MediaTypeFormatter), TypeAssert.TypeProperties.IsPublicVisibleClass | TypeAssert.TypeProperties.IsAbstract); } [Fact] public void Constructor() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; Assert.NotNull(supportedMediaTypes); Assert.Empty(supportedMediaTypes); Collection mappings = formatter.MediaTypeMappings; Assert.NotNull(mappings); Assert.Empty(mappings); } [Fact] void CopyConstructor() { TestMediaTypeFormatter formatter = new TestMediaTypeFormatter(); TestMediaTypeFormatter derivedFormatter = new TestMediaTypeFormatter(formatter); Assert.Same(formatter.MediaTypeMappings, derivedFormatter.MediaTypeMappings); Assert.Same(formatter.MediaTypeMappingsInternal, derivedFormatter.MediaTypeMappingsInternal); Assert.Equal(formatter.RequiredMemberSelector, derivedFormatter.RequiredMemberSelector); Assert.Same(formatter.SupportedMediaTypes, derivedFormatter.SupportedMediaTypes); Assert.Same(formatter.SupportedMediaTypesInternal, derivedFormatter.SupportedMediaTypesInternal); Assert.Same(formatter.SupportedEncodings, derivedFormatter.SupportedEncodings); Assert.Same(formatter.SupportedEncodingsInternal, derivedFormatter.SupportedEncodingsInternal); } [Fact] public void MaxCollectionKeySize_RoundTrips() { var defaultMaxKeys = MediaTypeFormatter.MaxHttpCollectionKeys; try { Assert.Reflection.IntegerProperty( null, c => MediaTypeFormatter.MaxHttpCollectionKeys, expectedDefaultValue: PlatformInfo.Platform == Platform.Net40 ? 1000 : Int32.MaxValue, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 125); } finally { MediaTypeFormatter.MaxHttpCollectionKeys = defaultMaxKeys; } } [Fact] public void SupportedMediaTypes_IsMutable() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; MediaTypeHeaderValue[] mediaTypes = HttpTestData.LegalMediaTypeHeaderValues.ToArray(); foreach (MediaTypeHeaderValue mediaType in mediaTypes) { supportedMediaTypes.Add(mediaType); } Assert.True(mediaTypes.SequenceEqual(formatter.SupportedMediaTypes)); } [Fact] public void SupportedMediaTypes_AddThrowsWithNullMediaType() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; Assert.ThrowsArgumentNull(() => supportedMediaTypes.Add(null), "item"); } [Theory] [TestDataSet(typeof(HttpTestData), "LegalMediaRangeValues")] public void SupportedMediaTypes_AddThrowsWithMediaRange(MediaTypeHeaderValue mediaType) { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; Assert.ThrowsArgument(() => supportedMediaTypes.Add(mediaType), "item", Error.Format(Properties.Resources.CannotUseMediaRangeForSupportedMediaType, typeof(MediaTypeHeaderValue).Name, mediaType.MediaType)); } [Fact] public void SupportedMediaTypes_InsertThrowsWithNullMediaType() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; Assert.ThrowsArgumentNull(() => supportedMediaTypes.Insert(0, null), "item"); } [Theory] [TestDataSet(typeof(HttpTestData), "LegalMediaRangeValues")] public void SupportedMediaTypes_InsertThrowsWithMediaRange(MediaTypeHeaderValue mediaType) { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection supportedMediaTypes = formatter.SupportedMediaTypes; Assert.ThrowsArgument(() => supportedMediaTypes.Insert(0, mediaType), "item", Error.Format(Properties.Resources.CannotUseMediaRangeForSupportedMediaType, typeof(MediaTypeHeaderValue).Name, mediaType.MediaType)); } [Fact] public void MediaTypeMappings_IsMutable() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Collection mappings = formatter.MediaTypeMappings; MediaTypeMapping[] standardMappings = HttpTestData.StandardMediaTypeMappings.ToArray(); foreach (MediaTypeMapping mapping in standardMappings) { mappings.Add(mapping); } Assert.True(standardMappings.SequenceEqual(formatter.MediaTypeMappings)); } [Fact] public void SelectCharacterEncoding_ThrowsIfNoSupportedEncodings() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter { CallBase = true }; HttpContent content = new StringContent("Hello World", Encoding.UTF8, "text/plain"); // Act Assert.Throws(() => formatter.SelectCharacterEncoding(content.Headers)); } [Theory] [PropertyData("SelectCharacterEncodingTestData")] public void SelectCharacterEncoding_ReturnsBestEncoding(string bodyEncoding, string[] supportedEncodings, string expectedEncoding) { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter { CallBase = true }; foreach (string supportedEncoding in supportedEncodings) { formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding)); } HttpContentHeaders contentHeaders = null; if (bodyEncoding != null) { Encoding bodyEnc = Encoding.GetEncoding(bodyEncoding); HttpContent content = new StringContent("Hello World", bodyEnc, "text/plain"); contentHeaders = content.Headers; } // Act Encoding actualEncoding = formatter.SelectCharacterEncoding(contentHeaders); // Assert Encoding expectedEnc = expectedEncoding != null ? Encoding.GetEncoding(expectedEncoding) : null; Assert.Equal(expectedEnc, actualEncoding); } [Fact] public void ReadFromStreamAsync_ThrowsNotSupportedException() { var formatter = new Mock { CallBase = true }.Object; Assert.Throws(() => formatter.ReadFromStreamAsync(null, null, null, null), "The media type formatter of type 'MediaTypeFormatterProxy' does not support reading because it does not implement the ReadFromStreamAsync method."); } [Fact] public void WriteToStreamAsync_ThrowsNotSupportedException() { var formatter = new Mock { CallBase = true }.Object; Assert.Throws(() => formatter.WriteToStreamAsync(null, null, null, null, null), "The media type formatter of type 'MediaTypeFormatterProxy' does not support writing because it does not implement the WriteToStreamAsync method."); } [Theory] [InlineData(typeof(object))] [InlineData(typeof(string))] [InlineData(typeof(Nullable))] public void GetDefaultValueForType_ReturnsNullForReferenceTypes(Type referenceType) { Assert.Null(MediaTypeFormatter.GetDefaultValueForType(referenceType)); } [Theory] [InlineData(false)] [InlineData(0)] [InlineData('a')] public void GetDefaultValueForType_ReturnsValueForValueTypes(T value) { Type valueType = value.GetType(); T defaultValue = default(T); Assert.Equal(defaultValue, MediaTypeFormatter.GetDefaultValueForType(valueType)); } [Fact] public void GetDefaultValueForType_ReturnsValueForStruct() { TestStruct s = new TestStruct(); TestStruct result = (TestStruct)MediaTypeFormatter.GetDefaultValueForType(typeof(TestStruct)); Assert.Equal(s, result); } [Fact] public void SetDefaultContentHeaders_ThrowsOnNullType() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); HttpContentHeaders contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); Assert.ThrowsArgumentNull(() => formatter.SetDefaultContentHeaders(null, contentHeaders, TestMediaTypeHeader), "type"); } [Fact] public void SetDefaultContentHeaders_ThrowsOnNullHeaders() { MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Type type = typeof(object); Assert.ThrowsArgumentNull(() => formatter.SetDefaultContentHeaders(type, null, TestMediaTypeHeader), "headers"); } [Fact] public void SetDefaultContentHeaders_UsesNonNullMediaTypeClone() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Type type = typeof(object); HttpContentHeaders contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); // Act formatter.SetDefaultContentHeaders(type, contentHeaders, TestMediaTypeHeader); // Assert Assert.NotSame(TestMediaTypeHeader, contentHeaders.ContentType); Assert.Equal(TestMediaType, contentHeaders.ContentType.MediaType); } [Fact] public void SetDefaultContentHeaders_UsesDefaultSupportedMediaTypeClone() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); formatter.SupportedMediaTypes.Add(TestMediaTypeHeader); Type type = typeof(object); HttpContentHeaders contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); // Act formatter.SetDefaultContentHeaders(type, contentHeaders, null); // Assert Assert.NotSame(TestMediaTypeHeader, contentHeaders.ContentType); Assert.Equal(TestMediaType, contentHeaders.ContentType.MediaType); } [Fact] public void SetDefaultContentHeaders_UsesDefaultSupportedEncoding() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); Encoding encoding = new UnicodeEncoding(); formatter.SupportedEncodings.Add(encoding); Type type = typeof(object); HttpContentHeaders contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); // Act formatter.SetDefaultContentHeaders(type, contentHeaders, TestMediaTypeHeader); // Assert Assert.Equal(TestMediaType, contentHeaders.ContentType.MediaType); Assert.Equal(encoding.WebName, contentHeaders.ContentType.CharSet); } [Fact] public void SetDefaultContentHeaders_UsesDefaultSupportedMediaTypeAndEncoding() { // Arrange MockMediaTypeFormatter formatter = new MockMediaTypeFormatter(); formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue(TestMediaType)); Encoding encoding = new UnicodeEncoding(); formatter.SupportedEncodings.Add(encoding); Type type = typeof(object); HttpContentHeaders contentHeaders = FormattingUtilities.CreateEmptyContentHeaders(); // Act formatter.SetDefaultContentHeaders(type, contentHeaders, null); // Assert Assert.Equal(TestMediaType, contentHeaders.ContentType.MediaType); Assert.Equal(encoding.WebName, contentHeaders.ContentType.CharSet); } [Fact] public async Task WriteToStreamAsyncWithCancellationToken_GetsCalled_DuringObjectContentWrite() { // Arrange object value = new object(); Type type = typeof(object); MemoryStream stream = new MemoryStream(); Mock formatter = new Mock{ CallBase = true }; formatter.Setup(f => f.CanWriteType(type)).Returns(true); formatter .Setup(f => f.WriteToStreamAsync(type, value, stream, It.IsAny(), null, CancellationToken.None)) .Returns(TaskHelpers.Completed()) .Verifiable(); ObjectContent content = new ObjectContent(type, value, formatter.Object); // Act await content.CopyToAsync(stream); // Assert formatter.Verify(); } public struct TestStruct { private int I; public TestStruct(int i) { I = i + 1; } } private class TestMediaTypeFormatter : MediaTypeFormatter { public TestMediaTypeFormatter() { } public TestMediaTypeFormatter(TestMediaTypeFormatter formatter) : base(formatter) { } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeHeaderValueExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class MediaTypeHeaderValueExtensionsTests { public static TheoryDataSet EqualValues { get { // These values are all equal return new TheoryDataSet { { "text/xml", "text/xml", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "TEXT/XML", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "text/xml; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml; parameter=value", "text/xml; parameter=value", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8; parameter=value", "text/xml; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/*", "text/*", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*", "TEXT/*", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*; charset=utf-8", "text/*; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*; parameter=value", "text/*; parameter=value", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*; charset=utf-8; parameter=value", "text/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "*/*", "*/*", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "*/*; charset=utf-8", "*/*; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "*/*; parameter=value", "*/*; parameter=value", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "*/*; charset=utf-8; parameter=value", "*/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, }; } } public static TheoryDataSet NonEqualValues { get { return new TheoryDataSet { // These values are all subsets. If compared in reverse they are all non-subsets. { "text/xml", "text/xml; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "TEXT/XML; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "text/xml; parameter=value", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "text/xml; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/*", "text/*; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*", "TEXT/*; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*", "text/*; parameter=value", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/*; charset=utf-8", "text/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/xml", "text/*", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/xml", "text/*; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/xml", "TEXT/*; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/xml", "text/*; parameter=value", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "text/xml; charset=utf-8", "text/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.SubtypeMediaRange }, { "*/*", "*/*; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "*/*", "*/*; parameter=value", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "*/*; charset=utf-8", "*/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/*", "*/*", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/*", "*/*; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/*", "*/*; parameter=value", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/*; charset=utf-8", "*/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/xml", "*/*", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/xml", "*/*; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/xml", "*/*; parameter=value", (int)MediaTypeHeaderValueRange.AllMediaRange }, { "text/xml; charset=utf-8", "*/*; parameter=value; charset=utf-8", (int)MediaTypeHeaderValueRange.AllMediaRange }, }; } } public static TheoryDataSet NonOverlappingValues { get { return new TheoryDataSet { // These values are all value1 < value2 regardless of which value is first and second // We do this asymmetric sorting algorithm to ensure that subsets are always <=0. { "text/xml", "application/xml", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "APPLICATION/XML", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "application/xml; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "application/xml; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "application/xml; parameter=value", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "text/plain", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "TEXT/PLAIN", (int)MediaTypeHeaderValueRange.None }, { "text/xml", "text/plain; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "text/plain; charset=utf-8", (int)MediaTypeHeaderValueRange.None }, { "text/xml; charset=utf-8", "text/plain; parameter=value", (int)MediaTypeHeaderValueRange.None }, }; } } [Theory] [PropertyData("EqualValues")] public void IsSubsetOf_ReturnsTrueForEqualValues(string mediaType1, string mediaType2, int range) { MediaTypeHeaderValue mediaTypeHeaderValue1 = MediaTypeHeaderValue.Parse(mediaType1); MediaTypeHeaderValue mediaTypeHeaderValue2 = MediaTypeHeaderValue.Parse(mediaType2); MediaTypeHeaderValueRange actualRange; Assert.True(mediaTypeHeaderValue1.IsSubsetOf(mediaTypeHeaderValue2, out actualRange)); Assert.Equal(range, (int)actualRange); Assert.True(mediaTypeHeaderValue2.IsSubsetOf(mediaTypeHeaderValue1, out actualRange)); } [Theory] [PropertyData("NonEqualValues")] public void IsSubsetOf_ReturnsTrueForNonEqualValues(string mediaType1, string mediaType2, int range) { MediaTypeHeaderValue mediaTypeHeaderValue1 = MediaTypeHeaderValue.Parse(mediaType1); MediaTypeHeaderValue mediaTypeHeaderValue2 = MediaTypeHeaderValue.Parse(mediaType2); MediaTypeHeaderValueRange actualRange; Assert.True(mediaTypeHeaderValue1.IsSubsetOf(mediaTypeHeaderValue2, out actualRange)); Assert.Equal(range, (int)actualRange); Assert.False(mediaTypeHeaderValue2.IsSubsetOf(mediaTypeHeaderValue1)); } [Theory] [PropertyData("NonOverlappingValues")] public void IsSubsetOf_ReturnsFalseForNonOverlappingValues(string mediaType1, string mediaType2, int range) { MediaTypeHeaderValue mediaTypeHeaderValue1 = MediaTypeHeaderValue.Parse(mediaType1); MediaTypeHeaderValue mediaTypeHeaderValue2 = MediaTypeHeaderValue.Parse(mediaType2); MediaTypeHeaderValueRange actualRange; Assert.False(mediaTypeHeaderValue1.IsSubsetOf(mediaTypeHeaderValue2, out actualRange)); Assert.Equal(range, (int)actualRange); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeMappingTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class MediaTypeMappingTests { [Fact] public void Constructor_ThrowsOnNullMediaTypeHeaderValue() { Assert.ThrowsArgumentNull(() => new MockMediaTypeMapping((MediaTypeHeaderValue)null), "mediaType"); } [Fact] public void Constructor_ThrowsOnNullMediaType() { Assert.ThrowsArgumentNull(() => new MockMediaTypeMapping((string)null), "mediaType"); Assert.ThrowsArgumentNull(() => new MockMediaTypeMapping(String.Empty), "mediaType"); } public class MockMediaTypeMapping : MediaTypeMapping { public MockMediaTypeMapping(MediaTypeHeaderValue mediaType) : base(mediaType) { } public MockMediaTypeMapping(string mediaType) : base(mediaType) { } public override double TryMatchMediaType(HttpRequestMessage request) { throw new NotImplementedException(); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/MediaTypeWithQualityHeaderValueComparerTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class MediaTypeWithQualityHeaderValueComparerTests { public static TheoryDataSet EqualValues { get { return new TheoryDataSet { { "text/xml", "text/xml" }, { "text/xml", "text/xml; q=1" }, { "text/xml", "text/xml; q=1.0" }, { "text/xml", "text/xml; q=1.0000" }, { "text/xml", "TEXT/XML" }, { "text/plain", "text/xml" }, { "text/*", "text/*" }, { "text/*", "TEXT/*" }, { "*/*", "*/*" }, { "text/xml", "text/xml; charset=utf8" }, { "text/xml", "text/xml; parameter=value" }, { "text/xml; parameter=value", "text/xml; parameter=value" }, { "text/xml; parameter1=value1", "text/xml; parameter2=value2" }, { "text/xml; q=0.5", "text/xml; q=0.50" }, { "application/xml; q=0.5", "text/xml; q=0.5" }, { "application/xml; q=0.1", "text/xml; q=0.1" }, { "application/xml; parameter=value1; q=0.5", "text/xml; parameter=value2; q=0.5" }, { "text/xml", "text/xml;q=1" }, { "text/xml", "text/xml;q=1.0" }, { "text/xml", "text/xml; q=1.00000" }, { "text/xml; q=0.5", "text/xml; q=0.5" }, { "text/xml; q=1.0", "text/xml; q=1.0" }, { "*/*", "*/*;q=1" }, { "*/*", "*/*; q=1" }, { "*/*", "*/*;q=1.0" }, { "*/*", "*/*; q=1.0" }, { "*/*; q=0.5", "*/*; q=0.5" }, { "*/*; q=1.0", "*/*; q=1.0" }, }; } } public static TheoryDataSet NonEqualValues { get { return new TheoryDataSet { { "text/plain; q=0.5", "text/plain" }, { "text/plain; q=0.5", "application/xml" }, { "text/plain; q=0.5", "text/plain; q=1.0" }, { "text/plain; q=0.5", "text/xml; q=1.0" }, { "text/*", "text/plain" }, { "application/*", "text/plain" }, { "*/*", "text/xml" }, { "*/*", "text/*" }, { "*/*;q=0.5", "*/*;q=0.6" }, { "*/*;q=0.5", "text/*;q=0.5" }, { "*/*;q=1", "text/plain" }, { "*/*; q=1", "text/plain" }, { "*/*;q=1.0", "text/plain" }, { "*/*; q=1.0", "text/plain" }, { "*/*; q=0.5", "text/plain; q=0.5" }, { "*/*; q=1.0", "text/plain; q=1.0" }, { "*/*; q=0.5", "text/*; q=0.6" }, }; } } public static TheoryDataSet BeforeAfterSortedValues { get { return new TheoryDataSet { { new string[] { "application/*", "text/plain", "text/plain;q=1.0", "text/plain", "text/plain;q=0", "*/*;q=0.8", "*/*;q=1", "text/*;q=1", "text/plain;q=0.8", "text/*;q=0.8", "text/*;q=0.6", "text/*;q=1.0", "*/*;q=0.4", "text/plain;q=0.6", "text/xml", }, new string[] { "text/plain", "text/plain;q=1.0", "text/plain", "text/xml", "application/*", "text/*;q=1", "text/*;q=1.0", "*/*;q=1", "text/plain;q=0.8", "text/*;q=0.8", "*/*;q=0.8", "text/plain;q=0.6", "text/*;q=0.6", "*/*;q=0.4", "text/plain;q=0", } } }; } } [Fact] public void StaticComparer_Returns_SameInstance() { MediaTypeWithQualityHeaderValueComparer comparer1 = MediaTypeWithQualityHeaderValueComparer.QualityComparer; MediaTypeWithQualityHeaderValueComparer comparer2 = MediaTypeWithQualityHeaderValueComparer.QualityComparer; Assert.NotNull(comparer1); Assert.Same(comparer1, comparer2); } [Theory] [PropertyData("EqualValues")] public void ComparerReturnsZeroForEqualValues(string mediaType1, string mediaType2) { // Arrange MediaTypeWithQualityHeaderValueComparer comparer = MediaTypeWithQualityHeaderValueComparer.QualityComparer; // Act MediaTypeWithQualityHeaderValue mediaTypeHeaderValue1 = MediaTypeWithQualityHeaderValue.Parse(mediaType1); MediaTypeWithQualityHeaderValue mediaTypeHeaderValue2 = MediaTypeWithQualityHeaderValue.Parse(mediaType2); // Assert Assert.Equal(0, comparer.Compare(mediaTypeHeaderValue1, mediaTypeHeaderValue2)); Assert.Equal(0, comparer.Compare(mediaTypeHeaderValue2, mediaTypeHeaderValue1)); } [Theory] [PropertyData("NonEqualValues")] public void ComparerReturnsNonZeroForNonEqualValues(string mediaType1, string mediaType2) { // Arrange MediaTypeWithQualityHeaderValueComparer comparer = MediaTypeWithQualityHeaderValueComparer.QualityComparer; // Act MediaTypeWithQualityHeaderValue mediaTypeHeaderValue1 = MediaTypeWithQualityHeaderValue.Parse(mediaType1); MediaTypeWithQualityHeaderValue mediaTypeHeaderValue2 = MediaTypeWithQualityHeaderValue.Parse(mediaType2); // Assert Assert.Equal(-1, comparer.Compare(mediaTypeHeaderValue1, mediaTypeHeaderValue2)); Assert.Equal(1, comparer.Compare(mediaTypeHeaderValue2, mediaTypeHeaderValue1)); } [Theory] [PropertyData("BeforeAfterSortedValues")] public void ComparerSortsListCorrectly(string[] unsorted, string[] expectedSorted) { // Arrange IEnumerable unsortedValues = unsorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)); IEnumerable expectedSortedValues = expectedSorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)); // Act IEnumerable actualSorted = unsortedValues.OrderByDescending(m => m, MediaTypeWithQualityHeaderValueComparer.QualityComparer); // Assert Assert.True(expectedSortedValues.SequenceEqual(actualSorted)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/ParsedMediaTypeHeaderValueTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class ParsedMediaTypeHeadeValueTests { public static TheoryDataSet FullMediaRanges { get { return new TheoryDataSet { "*/*", "*/*; charset=utf-8", "*/*; charset=utf-8; q=1.0", }; } } public static TheoryDataSet SubTypeMediaRanges { get { return new TheoryDataSet { "text/*", "TEXT/*", "application/*; charset=utf-8", "APPLICATION/*; charset=utf-8", "APPLICATION/*; charset=utf-8; q=1.0", }; } } public static TheoryDataSet InvalidMediaRanges { get { return new TheoryDataSet { "*/text", "*/XML", }; } } public static TheoryDataSet NonMediaRanges { get { return new TheoryDataSet { "text/plain", "TEXT/XML", "application/xml; charset=utf-8", "APPLICATION/xml; charset=utf-8", }; } } public static TheoryDataSet InvalidNonMediaRanges { get { return new TheoryDataSet { "", " ", "\n", "\t", "text", "text/", "text\\", "\\", "//", "text/[", "text/ ", " text/", " text/ ", "text\\ ", " text\\", " text\\ ", "text\\xml", "text//xml" }; } } [Theory] [PropertyData("InvalidNonMediaRanges")] public void MediaTypeHeaderValue_EnsuresValidMediaType(string invalidMediaType) { Assert.Throws(() => new MediaTypeHeaderValue(invalidMediaType), exceptionMessage: null, allowDerivedExceptions: true); } [Fact] public void TypesEqual_SameType_True() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/json")); Assert.True(parsedMediaType1.TypesEqual(ref parsedMediaType2)); } [Fact] public void TypesEqual_SameTypeDifferentCase_True() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("TEXT/xml")); Assert.True(parsedMediaType1.TypesEqual(ref parsedMediaType2)); } [Fact] public void TypesEqual_DifferentType_False() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("application/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); Assert.False(parsedMediaType1.TypesEqual(ref parsedMediaType2)); } [Fact] public void TypesEqual_DifferentTypeSameLength_False() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("texx/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); Assert.False(parsedMediaType1.TypesEqual(ref parsedMediaType2)); } [Fact] public void SubTypesEqual_SameType_True() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("application/xml")); Assert.True(parsedMediaType1.SubTypesEqual(ref parsedMediaType2)); } [Fact] public void SubTypesEqual_SameTypeDifferentCase_True() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/XML")); Assert.True(parsedMediaType1.SubTypesEqual(ref parsedMediaType2)); } [Fact] public void SubTypesEqual_DifferentType_False() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/json")); Assert.False(parsedMediaType1.SubTypesEqual(ref parsedMediaType2)); } [Fact] public void SubTypesEqual_DifferentTypeSameLength_False() { ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/xml")); ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(MediaTypeHeaderValue.Parse("text/yml")); Assert.False(parsedMediaType1.SubTypesEqual(ref parsedMediaType2)); } [Theory] [PropertyData("FullMediaRanges")] [PropertyData("SubTypeMediaRanges")] public void IsSubTypeMediaRange_ReturnsTrueForSubTypeMediaRanges(string mediaType) { MediaTypeHeaderValue mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(mediaType); ParsedMediaTypeHeaderValue parsedMediaType = new ParsedMediaTypeHeaderValue(mediaTypeHeaderValue); Assert.True(parsedMediaType.IsSubtypeMediaRange); } [Theory] [PropertyData("InvalidMediaRanges")] [PropertyData("NonMediaRanges")] public void IsSubTypeMediaRange_ReturnsFalseForNonSubTypeMediaRanges(string mediaType) { MediaTypeHeaderValue mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(mediaType); ParsedMediaTypeHeaderValue parsedMediaType = new ParsedMediaTypeHeaderValue(mediaTypeHeaderValue); Assert.False(parsedMediaType.IsSubtypeMediaRange); } [Theory] [PropertyData("FullMediaRanges")] public void IsAllMediaRange_ReturnsTrueForFullMediaTypeRanges(string mediaType) { MediaTypeHeaderValue mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(mediaType); ParsedMediaTypeHeaderValue parsedMediaType = new ParsedMediaTypeHeaderValue(mediaTypeHeaderValue); Assert.True(parsedMediaType.IsAllMediaRange); } [Theory] [PropertyData("SubTypeMediaRanges")] [PropertyData("InvalidMediaRanges")] [PropertyData("NonMediaRanges")] public void IsAllMediaRange_ReturnsFalseForNonFullMediaTypeRanges(string mediaType) { MediaTypeHeaderValue mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(mediaType); ParsedMediaTypeHeaderValue parsedMediaType = new ParsedMediaTypeHeaderValue(mediaTypeHeaderValue); Assert.False(parsedMediaType.IsAllMediaRange); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/FormUrlEncodedParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class FormUrlEncodedParserTests { private const int MinMessageSize = 1; private const int Iterations = 16; internal static Collection> CreateCollection() { return new Collection>(); } internal static FormUrlEncodedParser CreateParser(int maxMessageSize, out ICollection> nameValuePairs) { nameValuePairs = CreateCollection(); return new FormUrlEncodedParser(nameValuePairs, maxMessageSize); } internal static byte[] CreateBuffer(params string[] nameValuePairs) { StringBuilder buffer = new StringBuilder(); bool first = true; foreach (var h in nameValuePairs) { if (first) { first = false; } else { buffer.Append('&'); } buffer.Append(h); } return Encoding.UTF8.GetBytes(buffer.ToString()); } internal static ParserState ParseBufferInSteps(FormUrlEncodedParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, totalBytesConsumed == buffer.Length - size); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void FormUrlEncodedParserThrowsOnNull() { Assert.ThrowsArgumentNull(() => { new FormUrlEncodedParser(null, ParserData.MinHeaderSize); }, "nameValuePairs"); } [Fact] public void FormUrlEncodedParserThrowsOnInvalidSize() { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { new FormUrlEncodedParser(CreateCollection(), MinMessageSize - 1); }, "maxMessageSize", MinMessageSize.ToString(), MinMessageSize - 1); FormUrlEncodedParser parser = new FormUrlEncodedParser(CreateCollection(), MinMessageSize); Assert.NotNull(parser); parser = new FormUrlEncodedParser(CreateCollection(), MinMessageSize + 1); Assert.NotNull(parser); } [Fact] public void ParseBufferThrowsOnNullBuffer() { ICollection> collection; FormUrlEncodedParser parser = CreateParser(128, out collection); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed, false); }, "buffer"); } [Fact] public void ParseBufferHandlesEmptyBuffer() { byte[] data = CreateBuffer(); ICollection> collection; FormUrlEncodedParser parser = CreateParser(MinMessageSize, out collection); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed, true); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); Assert.Empty(collection); } [Theory] [InlineData("N", "N", "")] [InlineData("%26", "&", "")] [TestDataSet(typeof(UriQueryTestData), "UriQueryData")] public void ParseBufferCorrectly(string segment, string name, string value) { for (int index = 1; index < Iterations; index++) { List segments = new List(); for (int cnt = 0; cnt < index; cnt++) { segments.Add(segment); } byte[] data = CreateBuffer(segments.ToArray()); for (var cnt = 1; cnt <= data.Length; cnt++) { ICollection> collection; FormUrlEncodedParser parser = CreateParser(data.Length + 1, out collection); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(index, collection.Count()); foreach (KeyValuePair element in collection) { Assert.Equal(name, element.Key); Assert.Equal(value, element.Value); } } } } [Fact] public void HeaderParserDataTooBig() { byte[] data = CreateBuffer("N=V"); ICollection> collection; FormUrlEncodedParser parser = CreateParser(MinMessageSize, out collection); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed, true); Assert.Equal(ParserState.DataTooBig, state); Assert.Equal(MinMessageSize, bytesConsumed); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/HttpRequestHeaderParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class HttpRequestHeaderParserTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void HttpRequestHeaderParserConstructorTest() { HttpUnsortedRequest result = new HttpUnsortedRequest(); Assert.NotNull(result); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize - 1, ParserData.MinHeaderSize), "maxRequestLineSize", ParserData.MinRequestLineSize.ToString(), ParserData.MinRequestLineSize - 1); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize - 1), "maxHeaderSize", ParserData.MinHeaderSize.ToString(), ParserData.MinHeaderSize - 1); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); Assert.ThrowsArgumentNull(() => { new HttpRequestHeaderParser(null); }, "httpRequest"); } [Fact] public void RequestHeaderParserNullBuffer() { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer"); } [Fact] public void RequestHeaderParserMinimumBuffer() { byte[] data = CreateBuffer("G", "/", "HTTP/1.1", null); HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); ValidateResult(result, "G", "/", new Version("1.1"), null); } [Theory] [TestDataSet(typeof(HttpTestData), "AllHttpMethods")] public void RequestHeaderParserAcceptsStandardMethods(HttpMethod method) { byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, method.ToString(), "/", new Version("1.1"), ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(HttpTestData), "CustomHttpMethods")] public void RequestHeaderParserAcceptsCustomMethods(HttpMethod method) { byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, method.ToString(), "/", new Version("1.1"), ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidMethods")] public void RequestHeaderParserRejectsInvalidMethod(string invalidMethod) { byte[] data = CreateBuffer(invalidMethod, "/", "HTTP/1.1", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidRequestUris")] public void RequestHeaderParserRejectsInvalidUri(string invalidRequestUri) { byte[] data = CreateBuffer("GET", invalidRequestUri, "HTTP/1.1", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "Versions")] public void RequestHeaderParserAcceptsValidVersion(Version version) { byte[] data = CreateBuffer("GET", "/", String.Format("HTTP/{0}", version.ToString(2)), ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, "GET", "/", version, ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidVersions")] public void RequestHeaderParserRejectsInvalidVersion(string invalidVersion) { byte[] data = CreateBuffer("GET", "/", invalidVersion, ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest result = new HttpUnsortedRequest(); HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } private static byte[] CreateBuffer(string method, string address, string version, Dictionary headers) { const string SP = " "; const string CRLF = "\r\n"; string lws = SP; StringBuilder request = new StringBuilder(); request.AppendFormat("{0}{1}{2}{3}{4}{5}", method, lws, address, lws, version, CRLF); if (headers != null) { foreach (var h in headers) { request.AppendFormat("{0}: {1}{2}", h.Key, h.Value, CRLF); } } request.Append(CRLF); return Encoding.UTF8.GetBytes(request.ToString()); } private static ParserState ParseBufferInSteps(HttpRequestHeaderParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } private static void ValidateResult( HttpUnsortedRequest requestLine, string method, string requestUri, Version version, Dictionary headers) { Assert.Equal(new HttpMethod(method), requestLine.Method); Assert.Equal(requestUri, requestLine.RequestUri); Assert.Equal(version, requestLine.Version); if (headers != null) { Assert.Equal(headers.Count, requestLine.HttpHeaders.Count()); foreach (var header in headers) { Assert.True(requestLine.HttpHeaders.Contains(header.Key), "Parsed header did not contain expected key " + header.Key); Assert.Equal(header.Value, requestLine.HttpHeaders.GetValues(header.Key).ElementAt(0)); } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/HttpRequestLineParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.DataSets; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class HttpRequestLineParserTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void HttpRequestLineParserConstructorTest() { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); Assert.NotNull(requestLine); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize - 1), "maxRequestLineSize", ParserData.MinRequestLineSize.ToString(), ParserData.MinRequestLineSize - 1); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize); Assert.NotNull(parser); Assert.ThrowsArgumentNull(() => { new HttpRequestLineParser(null, ParserData.MinRequestLineSize); }, "httpRequest"); } [Fact] public void RequestLineParserNullBuffer() { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize); Assert.NotNull(parser); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer"); } [Fact] public void RequestLineParserMinimumBuffer() { byte[] data = CreateBuffer("G", "/", "HTTP/1.1"); HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize); Assert.NotNull(parser); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); ValidateResult(requestLine, "G", "/", new Version("1.1")); } [Fact] public void RequestLineParserRejectsLws() { byte[] data = CreateBuffer("GET", "/", "HTTP/1.1", true); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(HttpTestData), "AllHttpMethods")] public void RequestLineParserAcceptsStandardMethods(HttpMethod method) { byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(requestLine, method.ToString(), "/", new Version("1.1")); } } [Theory] [TestDataSet(typeof(HttpTestData), "CustomHttpMethods")] public void RequestLineParserAcceptsCustomMethods(HttpMethod method) { byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(requestLine, method.ToString(), "/", new Version("1.1")); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidMethods")] public void RequestLineParserRejectsInvalidMethod(string invalidMethod) { byte[] data = CreateBuffer(invalidMethod, "/", "HTTP/1.1"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidRequestUris")] public void RequestLineParserRejectsInvalidUri(string invalidRequestUri) { byte[] data = CreateBuffer("GET", invalidRequestUri, "HTTP/1.1"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "Versions")] public void RequestLineParserAcceptsValidVersion(Version version) { byte[] data = CreateBuffer("GET", "/", String.Format("HTTP/{0}", version.ToString(2))); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(requestLine, "GET", "/", version); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidVersions")] public void RequestLineParserRejectsInvalidVersion(string invalidVersion) { byte[] data = CreateBuffer("GET", "/", invalidVersion); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedRequest requestLine = new HttpUnsortedRequest(); HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } internal static byte[] CreateBuffer(string method, string address, string version) { return CreateBuffer(method, address, version, false); } private static byte[] CreateBuffer(string method, string address, string version, bool withLws) { const string SP = " "; const string HTAB = "\t"; const string CRLF = "\r\n"; string lws = SP; if (withLws) { lws = SP + SP + HTAB + SP; } string requestLine = String.Format("{0}{1}{2}{3}{4}{5}", method, lws, address, lws, version, CRLF); return Encoding.UTF8.GetBytes(requestLine); } private static ParserState ParseBufferInSteps(HttpRequestLineParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } private static void ValidateResult(HttpUnsortedRequest requestLine, string method, string requestUri, Version version) { Assert.Equal(new HttpMethod(method), requestLine.Method); Assert.Equal(requestUri, requestLine.RequestUri); Assert.Equal(version, requestLine.Version); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/HttpResponseHeaderParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class HttpResponseHeaderParserTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void HttpResponseHeaderParserConstructorTest() { HttpUnsortedResponse result = new HttpUnsortedResponse(); Assert.NotNull(result); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize - 1, ParserData.MinHeaderSize), "maxStatusLineSize", ParserData.MinStatusLineSize.ToString(), ParserData.MinStatusLineSize - 1); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize - 1), "maxHeaderSize", ParserData.MinHeaderSize.ToString(), ParserData.MinHeaderSize - 1); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); Assert.ThrowsArgumentNull(() => { new HttpResponseHeaderParser(null); }, "httpResponse"); } [Fact] public void ResponseHeaderParserNullBuffer() { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer"); } [Fact] public void ResponseHeaderParserMinimumBuffer() { byte[] data = CreateBuffer("HTTP/1.1", "200", "", null); HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize); Assert.NotNull(parser); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); ValidateResult(result, new Version("1.1"), HttpStatusCode.OK, "", null); } [Theory] [TestDataSet(typeof(HttpTestData), "AllHttpStatusCodes")] public void ResponseHeaderParserAcceptsStandardStatusCodes(HttpStatusCode status) { byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, new Version("1.1"), status, "Reason", ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(HttpTestData), "CustomHttpStatusCodes")] public void ResponseHeaderParserAcceptsCustomStatusCodes(HttpStatusCode status) { byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, new Version("1.1"), status, "Reason", ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidStatusCodes")] public void ResponseHeaderParserRejectsInvalidStatusCodes(string invalidStatus) { byte[] data = CreateBuffer("HTTP/1.1", invalidStatus, "Reason", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidReasonPhrases")] public void ResponseHeaderParserRejectsInvalidReasonPhrase(string invalidReason) { byte[] data = CreateBuffer("HTTP/1.1", "200", invalidReason, ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "Versions")] public void ResponseHeaderParserAcceptsValidVersion(Version version) { byte[] data = CreateBuffer(String.Format("HTTP/{0}", version.ToString(2)), "200", "Reason", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(result, version, HttpStatusCode.OK, "Reason", ParserData.ValidHeaders); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidVersions")] public void ResponseHeaderParserRejectsInvalidVersion(string invalid) { byte[] data = CreateBuffer(invalid, "200", "Reason", ParserData.ValidHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse result = new HttpUnsortedResponse(); HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } private static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase, Dictionary headers) { const string SP = " "; const string CRLF = "\r\n"; string lws = SP; StringBuilder response = new StringBuilder(); response.AppendFormat("{0}{1}{2}{3}{4}{5}", version, lws, statusCode, lws, reasonPhrase, CRLF); if (headers != null) { foreach (var h in headers) { response.AppendFormat("{0}: {1}{2}", h.Key, h.Value, CRLF); } } response.Append(CRLF); return Encoding.UTF8.GetBytes(response.ToString()); } private static ParserState ParseBufferInSteps(HttpResponseHeaderParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } private static void ValidateResult( HttpUnsortedResponse statusLine, Version version, HttpStatusCode statusCode, string reasonPhrase, Dictionary headers) { Assert.Equal(version, statusLine.Version); Assert.Equal(statusCode, statusLine.StatusCode); Assert.Equal(reasonPhrase, statusLine.ReasonPhrase); if (headers != null) { Assert.Equal(headers.Count, statusLine.HttpHeaders.Count()); foreach (var header in headers) { Assert.True(statusLine.HttpHeaders.Contains(header.Key), "Parsed header did not contain expected key " + header.Key); Assert.Equal(header.Value, statusLine.HttpHeaders.GetValues(header.Key).ElementAt(0)); } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/HttpStatusLineParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.DataSets; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class HttpStatusLineParserTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void HttpStatusLineParserConstructorTest() { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); Assert.NotNull(statusLine); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize - 1), "maxStatusLineSize", ParserData.MinStatusLineSize.ToString(), ParserData.MinStatusLineSize - 1); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize); Assert.NotNull(parser); Assert.ThrowsArgumentNull(() => { new HttpStatusLineParser(null, ParserData.MinStatusLineSize); }, "httpResponse"); } [Fact] public void StatusLineParserNullBuffer() { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize); Assert.NotNull(parser); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer"); } [Fact] public void StatusLineParserMinimumBuffer() { byte[] data = CreateBuffer("HTTP/1.1", "200", ""); HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize); Assert.NotNull(parser); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); ValidateResult(statusLine, new Version("1.1"), HttpStatusCode.OK, ""); } [Fact] public void StatusLineParserRejectsLws() { byte[] data = CreateBuffer("HTTP/1.1", "200", "Reason", true); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(HttpTestData), "AllHttpStatusCodes")] public void StatusLineParserAcceptsStandardStatusCodes(HttpStatusCode status) { byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(statusLine, new Version("1.1"), status, "Reason"); } } [Theory] [TestDataSet(typeof(HttpTestData), "CustomHttpStatusCodes")] public void StatusLineParserAcceptsCustomStatusCodes(HttpStatusCode status) { byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(statusLine, new Version("1.1"), status, "Reason"); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidStatusCodes")] public void StatusLineParserRejectsInvalidStatusCodes(string invalidStatus) { byte[] data = CreateBuffer("HTTP/1.1", invalidStatus, "Reason"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } [Theory] [TestDataSet(typeof(ParserData), "ValidReasonPhrases")] public void StatusLineParserAcceptsValidReasonPhrase(string validReasonPhrase) { byte[] data = CreateBuffer("HTTP/1.1", "200", validReasonPhrase); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); ValidateResult(statusLine, new Version("1.1"), HttpStatusCode.OK, validReasonPhrase); } } [Theory] [TestDataSet(typeof(ParserData), "Versions")] public void StatusLineParserAcceptsValidVersion(Version version) { byte[] data = CreateBuffer(String.Format("HTTP/{0}", version.ToString(2)), "200", "Reason"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(data.Length, totalBytesConsumed); ValidateResult(statusLine, version, HttpStatusCode.OK, "Reason"); } } [Theory] [TestDataSet(typeof(ParserData), "InvalidVersions")] public void StatusLineParserRejectsInvalidVersion(string invalidVersion) { byte[] data = CreateBuffer(invalidVersion, "200", "Reason"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpUnsortedResponse statusLine = new HttpUnsortedResponse(); HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); } } internal static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase) { return CreateBuffer(version, statusCode, reasonPhrase, false); } private static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase, bool withLws) { const string SP = " "; const string HTAB = "\t"; const string CRLF = "\r\n"; string lws = SP; if (withLws) { lws = SP + SP + HTAB + SP; } string statusLine = String.Format("{0}{1}{2}{3}{4}{5}", version, lws, statusCode, lws, reasonPhrase, CRLF); return Encoding.UTF8.GetBytes(statusLine); } private static ParserState ParseBufferInSteps(HttpStatusLineParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } private static void ValidateResult(HttpUnsortedResponse statusLine, Version version, HttpStatusCode statusCode, string reasonPhrase) { Assert.Equal(version, statusLine.Version); Assert.Equal(statusCode, statusLine.StatusCode); Assert.Equal(reasonPhrase, statusLine.ReasonPhrase); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/InternetMessageFormatHeaderParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using System.Text; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class InternetMessageFormatHeaderParserTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsClass); } [Fact] public void HeaderParserConstructorTest() { IEnumerable headers = InternetMessageFormatHeaderParserTests.CreateHttpHeaders(); foreach (var header in headers) { InternetMessageFormatHeaderParser parser = new InternetMessageFormatHeaderParser(header, ParserData.MinHeaderSize); Assert.NotNull(parser); } Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new InternetMessageFormatHeaderParser(headers.ElementAt(0), ParserData.MinHeaderSize - 1), "maxHeaderSize", ParserData.MinHeaderSize.ToString(), ParserData.MinHeaderSize - 1); Assert.ThrowsArgumentNull(() => { new InternetMessageFormatHeaderParser(null, ParserData.MinHeaderSize); }, "headers"); } [Fact] public void HeaderParserNullBuffer() { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(128, out headers); Assert.NotNull(parser); int bytesConsumed = 0; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer"); } [Fact] public void HeaderParserEmptyBuffer() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer(); HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int bytesConsumed = 0; ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, bytesConsumed); Assert.Empty(headers); } [Fact] public void HeaderParserSingleNameValueHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N:V"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Single(headers); IEnumerable parsedValues = headers.GetValues("N"); string parsedValue = Assert.Single(parsedValues); Assert.Equal("V", parsedValue); } } [Fact] public void HeaderParserSingleNameHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N:"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Single(headers); IEnumerable parsedValues = headers.GetValues("N"); string parsedValue = Assert.Single(parsedValues); Assert.Equal("", parsedValue); } } [Fact] public void HeaderParserMultipleNameHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N:V1", "N:V2"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Single(headers); IEnumerable parsedValues = headers.GetValues("N"); Assert.Equal(2, parsedValues.Count()); Assert.Equal("V1", parsedValues.ElementAt(0)); Assert.Equal("V2", parsedValues.ElementAt(1)); } } [Fact] public void HeaderParserLwsHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N1:V1", "N2: V2", "N3:\tV3"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(3, headers.Count()); IEnumerable parsedValues = headers.GetValues("N1"); string parsedValue = Assert.Single(parsedValues); Assert.Equal("V1", parsedValue); parsedValues = headers.GetValues("N2"); parsedValue = Assert.Single(parsedValues); Assert.Equal("V2", parsedValue); parsedValues = headers.GetValues("N3"); parsedValue = Assert.Single(parsedValues); Assert.Equal("V3", parsedValue); } } [Fact] public void HeaderParserInvalidHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N1 :V1"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Invalid, state); Assert.Equal(data.Length - 2, totalBytesConsumed); } } [Fact] public void HeaderParserSpecializedHeaders() { Dictionary headerData = new Dictionary { { @"JsonProperties0", @"{ ""SessionId"": ""{27729E1-B37B-4D29-AA0A-E367906C206E}"", ""MessageId"": ""{701332E1-B37B-4D29-AA0A-E367906C206E}"", ""TimeToLive"" : 90, ""CorrelationId"": ""{701332F3-B37B-4D29-AA0A-E367906C206E}"", ""SequenceNumber"" : 12345, ""DeliveryCount"" : 2, ""To"" : ""http://contoso.com/path1"", ""ReplyTo"" : ""http://fabrikam.com/path1"", ""SentTimeUtc"" : ""Sun, 06 Nov 1994 08:49:37 GMT"", ""ScheduledEnqueueTimeUtc"" : ""Sun, 06 Nov 1994 08:49:37 GMT""}" }, { @"JsonProperties1", @"{ ""SessionId"": ""{2813D4D2-46A9-4F4D-8904-E9BDE3712B70}"", ""MessageId"": ""{24AE31D6-63B8-46F3-9975-A3DAF1B6D3F4}"", ""TimeToLive"" : 80, ""CorrelationId"": ""{896DD5BD-1645-44D7-9E7C-D7F70958ECD6}"", ""SequenceNumber"" : 54321, ""DeliveryCount"" : 4, ""To"" : ""http://contoso.com/path2"", ""ReplyTo"" : ""http://fabrikam.com/path2"", ""SentTimeUtc"" : ""Sun, 06 Nov 1994 10:49:37 GMT"", ""ScheduledEnqueueTimeUtc"" : ""Sun, 06 Nov 1994 10:49:37 GMT""}" }, { @"P3P", @"CP=""ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI""" }, { @"Cookie", @"omniID=1297715979621_9f45_1519_3f8a_f22f85346ac6; WT_FPC=id=65.55.227.138-2323234032.30136233:lv=1309374389020:ss=1309374389020; A=I&I=AxUFAAAAAACNCAAADYEZ7CFPss7Swnujy4PXZA!!&M=1&CS=126mAa0002ZB51a02gZB51a; MC1=GUID=568428660ad44d4ab8f46133f4b03738&HASH=6628&LV=20113&V=3; WT_NVR_RU=0=msdn:1=:2=; MUID=A44DE185EA1B4E8088CCF7B348C5D65F; MSID=Microsoft.CreationDate=03/04/2011 23:38:15&Microsoft.LastVisitDate=06/20/2011 04:15:08&Microsoft.VisitStartDate=06/20/2011 04:15:08&Microsoft.CookieId=f658f3f2-e6d6-42ab-b86b-96791b942b6f&Microsoft.TokenId=ffffffff-ffff-ffff-ffff-ffffffffffff&Microsoft.NumberOfVisits=106&Microsoft.CookieFirstVisit=1&Microsoft.IdentityToken=AA==&Microsoft.MicrosoftId=0441-6141-1523-9969; msresearch=%7B%22version%22%3A%224.6%22%2C%22state%22%3A%7B%22name%22%3A%22IDLE%22%2C%22url%22%3Aundefined%2C%22timestamp%22%3A1299281911415%7D%2C%22lastinvited%22%3A1299281911415%2C%22userid%22%3A%2212992819114151265672533023080%22%2C%22vendorid%22%3A1%2C%22surveys%22%3A%5Bundefined%5D%7D; CodeSnippetContainerLang=C#; msdn=L=1033; ADS=SN=175A21EF; s_cc=true; s_sq=%5B%5BB%5D%5D; TocHashCookie=ms310241(n)/aa187916(n)/aa187917(n)/dd273952(n)/dd295083(n)/ff472634(n)/ee667046(n)/ee667070(n)/gg259047(n)/gg618436(n)/; WT_NVR=0=/:1=query|library|en-us:2=en-us/vcsharp|en-us/library" }, { @"Set-Cookie", @"A=I&I=AxUFAAAAAADsBgAA1sWZz4FGun/kOeyV4LGZVg!!&M=1; domain=.microsoft.com; expires=Sun, 30-Jun-2041 00:14:40 GMT; path=/" }, }; byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer(headerData.Select((kv) => { return String.Format("{0}: {1}", kv.Key, kv.Value); }).ToArray()); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(headerData.Count, headers.Count()); for (int hCnt = 0; hCnt < headerData.Count; hCnt++) { Assert.Equal(headerData.Keys.ElementAt(hCnt), headers.ElementAt(hCnt).Key); Assert.Equal(headerData.Values.ElementAt(hCnt), headers.ElementAt(hCnt).Value.ElementAt(0)); } } } [Fact] public void HeaderParserSplitHeader() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N:V1,", " V2,", "\tV3,", " V4,", " \tV5"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Single(headers); IEnumerable parsedValues = headers.GetValues("N"); string parsedValue = Assert.Single(parsedValues); Assert.Equal("V1, V2, V3, V4, \tV5", parsedValues.ElementAt(0)); } } [Fact] public void HeaderParserDataTooBigSingle() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N:V"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(ParserData.MinHeaderSize, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.DataTooBig, state); Assert.Equal(ParserData.MinHeaderSize, totalBytesConsumed); } } [Fact] public void HeaderParserTestDataTooBigMulti() { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer("N1:V1", "N2:V2", "N3:V3"); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(10, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.DataTooBig, state); Assert.Equal(10, totalBytesConsumed); } } [Fact] public void Rfc5322Sample1Test() { RunRfc5322SampleTest(Rfc5322Sample1, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample2Test() { RunRfc5322SampleTest(Rfc5322Sample2, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("sender")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample3Test() { RunRfc5322SampleTest(Rfc5322Sample3, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("cc")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample4Test() { RunRfc5322SampleTest(Rfc5322Sample4, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("cc")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample5Test() { RunRfc5322SampleTest(Rfc5322Sample5, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample6Test() { RunRfc5322SampleTest(Rfc5322Sample6, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("reply-to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); Assert.True(headers.Contains("in-reply-to")); Assert.True(headers.Contains("references")); }); } [Fact] public void Rfc5322Sample7Test() { RunRfc5322SampleTest(Rfc5322Sample7, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); Assert.True(headers.Contains("in-reply-to")); Assert.True(headers.Contains("references")); }); } [Fact] public void Rfc5322Sample8Test() { RunRfc5322SampleTest(Rfc5322Sample8, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample9Test() { RunRfc5322SampleTest(Rfc5322Sample9, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("resent-from")); Assert.True(headers.Contains("resent-to")); Assert.True(headers.Contains("resent-date")); Assert.True(headers.Contains("resent-message-id")); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample10Test() { RunRfc5322SampleTest(Rfc5322Sample10, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("received")); Assert.Equal(2, headers.GetValues("received").Count()); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("subject")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } [Fact] public void Rfc5322Sample11Test() { RunRfc5322SampleTest(Rfc5322Sample11, (headers) => { Assert.NotNull(headers); Assert.True(headers.Contains("from")); Assert.True(headers.Contains("to")); Assert.True(headers.Contains("cc")); Assert.True(headers.Contains("date")); Assert.True(headers.Contains("message-id")); }); } // Set of samples from RFC 5322 with times adjusted to GMT following HTTP style for date time format. static readonly string[] Rfc5322Sample1 = new string[] { @"From: John Doe ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample2 = new string[] { @"From: John Doe ", @"Sender: Michael Jones ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample3 = new string[] { @"From: ""Joe Q. Public"" ", @"To: Mary Smith , jdoe@example.org, Who? ", @"Cc: , ""Giant; \""Big\"" Box"" ", @"Date: Tue, 01 Jul 2003 10:52:37 GMT", @"Message-ID: <5678.21-Nov-1997@example.com>", }; static readonly string[] Rfc5322Sample4 = new string[] { @"From: Pete ", @"To: A Group:Ed Jones ,joe@where.test,John ;", @"Cc: Undisclosed recipients:;", @"Date: Thu, 13 Feb 1969 23:32:54 GMT", @"Message-ID: ", }; static readonly string[] Rfc5322Sample5 = new string[] { @"From: John Doe ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample6 = new string[] { @"From: Mary Smith ", @"To: John Doe ", @"Reply-To: ""Mary Smith: Personal Account"" ", @"Subject: Re: Saying Hello", @"Date: Fri, 21 Nov 1997 10:01:10 GMT", @"Message-ID: <3456@example.net>", @"In-Reply-To: <1234@local.machine.example>", @"References: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample7 = new string[] { @"To: ""Mary Smith: Personal Account"" ", @"From: John Doe ", @"Subject: Re: Saying Hello", @"Date: Fri, 21 Nov 1997 11:00:00 GMT", @"Message-ID: ", @"In-Reply-To: <3456@example.net>", @"References: <1234@local.machine.example> <3456@example.net>", }; static readonly string[] Rfc5322Sample8 = new string[] { @"From: John Doe ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample9 = new string[] { @"Resent-From: Mary Smith ", @"Resent-To: Jane Brown ", @"Resent-Date: Mon, 24 Nov 1997 14:22:01 GMT", @"Resent-Message-ID: <78910@example.net>", @"From: John Doe ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.machine.example>", }; static readonly string[] Rfc5322Sample10 = new string[] { @"Received: from x.y.test", @" by example.net", @" via TCP", @" with ESMTP", @" id ABC12345", @" for ; 21 Nov 1997 10:05:43 GMT", @"Received: from node.example by x.y.test; 21 Nov 1997 10:01:22 GMT", @"From: John Doe ", @"To: Mary Smith ", @"Subject: Saying Hello", @"Date: Fri, 21 Nov 1997 09:55:06 GMT", @"Message-ID: <1234@local.node.example>", }; static readonly string[] Rfc5322Sample11 = new string[] { @"From: Pete(A nice \) chap) ", @"To:A Group(Some people)", @" :Chris Jones ,", @" joe@example.org,", @" John (my dear friend); (the end of the group)", @"Cc:(Empty list)(start)Hidden recipients :(nobody(that I know)) ;", @"Date: Thu,", @" 13", @" Feb", @" 1969", @" 23:32:00", @" GMT", @"Message-ID: ", }; private static IEnumerable CreateHttpHeaders() { return new HttpHeaders[] { new HttpRequestMessage().Headers, new HttpResponseMessage().Headers, new StringContent(String.Empty).Headers, }; } private static InternetMessageFormatHeaderParser CreateHeaderParser(int maximumHeaderLength, out HttpHeaders headers) { headers = new HttpRequestMessage().Headers; return new InternetMessageFormatHeaderParser(headers, maximumHeaderLength); } internal static byte[] CreateBuffer(params string[] headers) { const string CRLF = "\r\n"; StringBuilder header = new StringBuilder(); foreach (var h in headers) { header.Append(h + CRLF); } header.Append(CRLF); return Encoding.UTF8.GetBytes(header.ToString()); } private static void RunRfc5322SampleTest(string[] testHeaders, Action validation) { byte[] data = InternetMessageFormatHeaderParserTests.CreateBuffer(testHeaders); for (var cnt = 1; cnt <= data.Length; cnt++) { HttpHeaders headers; InternetMessageFormatHeaderParser parser = InternetMessageFormatHeaderParserTests.CreateHeaderParser(data.Length, out headers); Assert.NotNull(parser); int totalBytesConsumed = 0; ParserState state = InternetMessageFormatHeaderParserTests.ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed); Assert.Equal(ParserState.Done, state); Assert.Equal(data.Length, totalBytesConsumed); validation(headers); } } private static ParserState ParseBufferInSteps(InternetMessageFormatHeaderParser parser, byte[] buffer, int readsize, out int totalBytesConsumed) { ParserState state = ParserState.Invalid; totalBytesConsumed = 0; while (totalBytesConsumed <= buffer.Length) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed); totalBytesConsumed += bytesConsumed; if (state != ParserState.NeedMoreData) { return state; } } return state; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/Parsers/MimeMultipartParserTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Formatting.Parsers { public class MimeMultipartParserTests { private const string SP = " "; private const string LF = "\n"; private const string CR = "\r"; private const string CRLF = "\r\n"; private const string HTAB = "\t"; private const string DashDash = "--"; public static TheoryDataSet Boundaries { get { return new TheoryDataSet { "1", "a", "'()+_--./:=?", "--", "----", "9df4e21f-6e6f-4b08-8023-97283d2afeeb", "-----------------------------7d159c1302d0y0", "--------------------01234567890123456789", "--------------------01234567890123456789--------------------", "--A--B--C--D--E--F--", }; } } public static TheoryDataSet SingleShortBodies { get { return new TheoryDataSet { "", "A", "AA", }; } } public static TheoryDataSet MultipleShortBodies { get { return new TheoryDataSet { CreateMultipleShortBodies("", 26), CreateMultipleShortBodies("{0}", 26), CreateMultipleShortBodies("--{0}", 26), }; } } public static TheoryDataSet SingleLongBodies { get { return new TheoryDataSet { CreateLongString("1234567890", "A", 128), CreateLongString("1234567890", "--", 128), }; } } public static TheoryDataSet MultipleLongBodies { get { string[] result = new string[16]; for (int count = 0; count < result.Length; count++) { string bookend = Char.ConvertFromUtf32(0x41 + count); result[count] = CreateLongString("1234567890", bookend, 16); } return new TheoryDataSet { result }; } } public static TheoryDataSet NearBoundaryBodies { get { return new TheoryDataSet { "AAA" + LF, "AAA" + CR, "AAA" + CRLF, "AAA" + CRLF + CRLF, "AAA" + CRLF + "-", "AAA" + CRLF + "-" + CR, "AAA" + CRLF + "=" + CRLF, CR + "-" + "AAA", CRLF + "-" + "AAA", CRLF + "--" + "AAA" + CR + "AAA", CRLF, "AAA", CRLF + CRLF, CRLF + CRLF + CRLF, "AAA" + "--" + "AAA", CRLF + "AAA" + "--" + "AAA" + "--", CRLF + "--" + "AAA" + CRLF, CRLF + "--" + "AAA" + CRLF + CRLF, CRLF + "--" + "AAA" + "--" + CRLF, CRLF + "--" + "AAA" + "--" + CRLF + CRLF, "--úN$(Os#»Í(Bt$(Dqf(CS'.‚æ0j", "--123456", "123--456", "123456--" }; } } public static TheoryDataSet TrueAndFalse { get { return new TheoryDataSet { true, false, }; } } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public void MimeMultipartParserConstructorTest(string boundary) { MimeMultipartParser parser = new MimeMultipartParser(boundary, ParserData.MinMessageSize); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new MimeMultipartParser("-", ParserData.MinMessageSize - 1), "maxMessageSize", ParserData.MinMessageSize.ToString(), ParserData.MinMessageSize - 1); foreach (string empty in TestData.EmptyStrings) { Assert.ThrowsArgument(() => { new MimeMultipartParser(empty, ParserData.MinMessageSize); }, "boundary", allowDerivedExceptions: true); } Assert.ThrowsArgument(() => { new MimeMultipartParser("trailingspace ", ParserData.MinMessageSize); }, "boundary"); Assert.ThrowsArgumentNull(() => { new MimeMultipartParser(null, ParserData.MinMessageSize); }, "boundary"); } [Fact] public void MimeMultipartParser_ThrowsOnTooBigBoundary() { string maxLegalBoundary = new string('a', 246); MimeMultipartParser parser = new MimeMultipartParser(maxLegalBoundary, ParserData.MinMessageSize); string minIllegalBoundary = new string('a', 247); Assert.ThrowsArgumentLessThanOrEqualTo(() => new MimeMultipartParser(minIllegalBoundary, ParserData.MinMessageSize), "boundary", "246", "247"); } [Fact] public void MultipartParserNullBuffer() { MimeMultipartParser parser = CreateMimeMultipartParser("-", 128); int bytesConsumed = 0; ArraySegment out1; ArraySegment out2; bool isFinal; Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed, out out1, out out2, out isFinal); }, "buffer"); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public void MultipartParserEmptyBuffer(string boundary) { byte[] data = CreateBuffer(boundary); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); Assert.Equal(0, bodyParts[1].Length); } } [Theory] [TestDataSet( typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "SingleShortBodies")] public void MultipartParserSingleShortBodyPart(string boundary, bool withExtraWhitespace, bool withExtraCRLF, string singleShortBody) { byte[] data = CreateBuffer(boundary, withExtraWhitespace, withExtraCRLF, singleShortBody); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); Assert.Equal(singleShortBody, bodyParts[1]); } } [Theory] [TestDataSet( typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "MultipleShortBodies")] public void MultipartParserMultipleShortBodyParts(string boundary, bool withExtraWhitespace, bool withExtraCRLF, string[] multipleShortBodies) { byte[] data = CreateBuffer(boundary, withExtraWhitespace, withExtraCRLF, multipleShortBodies); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(multipleShortBodies.Length + 1, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); for (var check = 0; check < multipleShortBodies.Length; check++) { Assert.Equal(multipleShortBodies[check], bodyParts[check + 1]); } } } [Theory] [TestDataSet( typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "SingleLongBodies")] public void MultipartParserSingleLongBodyPart(string boundary, bool withExtraWhitespace, bool withExtraCRLF, string singleLongBody) { byte[] data = CreateBuffer(boundary, withExtraWhitespace, withExtraCRLF, singleLongBody); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); Assert.Equal(singleLongBody.Length, bodyParts[1].Length); Assert.Equal(singleLongBody, bodyParts[1]); } } [Theory] [TestDataSet( typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "MultipleLongBodies")] public void MultipartParserMultipleLongBodyParts(string boundary, bool withExtraWhitespace, bool withExtraCRLF, string[] multipleLongBodies) { byte[] data = CreateBuffer(boundary, withExtraWhitespace, withExtraCRLF, multipleLongBodies); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(multipleLongBodies.Length + 1, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); for (var check = 0; check < multipleLongBodies.Length; check++) { Assert.Equal(multipleLongBodies[check], bodyParts[check + 1]); } } } [Theory] [TestDataSet( typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "TrueAndFalse", typeof(MimeMultipartParserTests), "NearBoundaryBodies")] public void MultipartParserNearMatches(string boundary, bool withExtraWhitespace, bool withExtraCRLF, string nearBoundaryBody) { byte[] data = CreateBuffer(boundary, withExtraWhitespace, withExtraCRLF, nearBoundaryBody); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); Assert.Equal(nearBoundaryBody, bodyParts[1]); } } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public void MultipartParserNesting(string boundary) { for (var nesting = 0; nesting < 16; nesting++) { string nested = CreateNestedBuffer(nesting); byte[] data = CreateBuffer(boundary, nested); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Equal(0, bodyParts[0].Length); Assert.Equal(nested.Length, bodyParts[1].Length); } } } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public void MimeMultipartParserTestDataTooBig(string boundary) { byte[] data = CreateBuffer(boundary); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary, ParserData.MinMessageSize); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.DataTooBig, state); Assert.Equal(ParserData.MinMessageSize, totalBytesConsumed); } } [Fact] public void MaxMessageSizeIsExact() { string boundary = "--A"; byte[] data = CreateBuffer(boundary, "cool"); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary, data.Length); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, 2, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(2, bodyParts.Count); Assert.Empty(bodyParts[0]); } } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public async Task MimeMultipartParserTestMultipartContent(string boundary) { MultipartContent content = new MultipartContent("mixed", boundary); content.Add(new StringContent("A")); content.Add(new StringContent("B")); content.Add(new StringContent("C")); MemoryStream memStream = new MemoryStream(); await content.CopyToAsync(memStream); memStream.Position = 0; byte[] data = memStream.ToArray(); for (var readSize = 1; readSize <= data.Length; readSize++) { MimeMultipartParser parser = CreateMimeMultipartParser(boundary); int totalBytesConsumed; List bodyParts; MimeMultipartParser.State state = ParseBufferInSteps(parser, data, readSize, out bodyParts, out totalBytesConsumed); Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state); Assert.Equal(data.Length, totalBytesConsumed); Assert.Equal(4, bodyParts.Count); Assert.Empty(bodyParts[0]); Assert.EndsWith("A", bodyParts[1]); Assert.EndsWith("B", bodyParts[2]); Assert.EndsWith("C", bodyParts[3]); } } private static MimeMultipartParser CreateMimeMultipartParser(string boundary) { return new MimeMultipartParser(boundary, MimeMultipartBodyPartParser.DefaultMaxMessageSize); } private static MimeMultipartParser CreateMimeMultipartParser(string boundary, int minimumLength) { return new MimeMultipartParser(boundary, minimumLength); } internal static byte[] CreateBuffer(string boundary, params string[] bodyparts) { return CreateBuffer(boundary, false, false, bodyparts); } internal static string CreateNestedBuffer(int count) { StringBuilder buffer = new StringBuilder("content"); for (var readSize = 0; readSize < count; readSize++) { byte[] nested = CreateBuffer("N" + readSize.ToString(), buffer.ToString()); var message = Encoding.UTF8.GetString(nested); buffer.Length = 0; buffer.AppendLine(message); } return buffer.ToString(); } private static byte[] CreateBuffer(string boundary, bool withExtraWhitespace, bool withTrailingCRLF, params string[] bodyparts) { string whitespace = String.Empty; if (withExtraWhitespace) { whitespace = SP + SP + HTAB + SP; } StringBuilder message = new StringBuilder(); message.Append(DashDash + boundary + whitespace + CRLF); for (var i = 0; i < bodyparts.Length; i++) { message.Append(bodyparts[i]); if (i < bodyparts.Length - 1) { message.Append(CRLF + DashDash + boundary + whitespace + CRLF); } } message.Append(CRLF + DashDash + boundary + DashDash + whitespace); if (withTrailingCRLF) { message.Append(CRLF); } return Encoding.UTF8.GetBytes(message.ToString()); } private static MimeMultipartParser.State ParseBufferInSteps(MimeMultipartParser parser, byte[] buffer, int readsize, out List bodyParts, out int totalBytesConsumed) { MimeMultipartParser.State state = MimeMultipartParser.State.Invalid; totalBytesConsumed = 0; bodyParts = new List(); bool isFinal = false; byte[] currentBodyPart = new byte[32 * 1024]; int currentBodyLength = 0; while (true) { int size = Math.Min(buffer.Length - totalBytesConsumed, readsize); byte[] parseBuffer = new byte[size]; Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size); int bytesConsumed = 0; ArraySegment out1; ArraySegment out2; state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, out out1, out out2, out isFinal); totalBytesConsumed += bytesConsumed; Buffer.BlockCopy(out1.Array, out1.Offset, currentBodyPart, currentBodyLength, out1.Count); currentBodyLength += out1.Count; Buffer.BlockCopy(out2.Array, out2.Offset, currentBodyPart, currentBodyLength, out2.Count); currentBodyLength += out2.Count; if (state == MimeMultipartParser.State.BodyPartCompleted) { var bPart = new byte[currentBodyLength]; Buffer.BlockCopy(currentBodyPart, 0, bPart, 0, currentBodyLength); bodyParts.Add(Encoding.UTF8.GetString(bPart)); currentBodyLength = 0; if (isFinal) { break; } } else if (state != MimeMultipartParser.State.NeedMoreData) { return state; } } Assert.True(isFinal, "The last segment is not a final segment."); return state; } private static string[] CreateMultipleShortBodies(string format, int iterations) { string[] result = new string[iterations]; for (int count = 0; count < iterations; count++) { result[count] = string.Format(format, Char.ConvertFromUtf32(0x41 + count)); } return result; } private static string CreateLongString(string msg, string bookend, int iterations) { StringBuilder longBody = new StringBuilder(); if (!String.IsNullOrEmpty(bookend)) { longBody.Append(bookend); } for (int i = 0; i < iterations; i++) { longBody.Append(msg); } if (!String.IsNullOrEmpty(bookend)) { longBody.Append(bookend); } return longBody.ToString(); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/QueryStringMappingTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Web.Http; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class QueryStringMappingTests { public static IEnumerable UriStringsWithoutQuery { get { return HttpTestData.UriTestDataStrings.Where((s) => !s.Contains('?')); } } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties( typeof(QueryStringMapping), TypeAssert.TypeProperties.IsPublicVisibleClass, typeof(MediaTypeMapping)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeHeaderValues")] public void Constructor(string queryStringParameterName, string queryStringParameterValue, MediaTypeHeaderValue mediaType) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); Assert.Equal(queryStringParameterName, mapping.QueryStringParameterName); Assert.Equal(queryStringParameterValue, mapping.QueryStringParameterValue); Assert.MediaType.AreEqual(mediaType, mapping.MediaType, "MediaType failed to set."); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeHeaderValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void ConstructorThrowsWithEmptyQueryParameterName(MediaTypeHeaderValue mediaType, string queryStringParameterName) { Assert.ThrowsArgumentNull(() => new QueryStringMapping(queryStringParameterName, "json", mediaType), "queryStringParameterName"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeHeaderValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void ConstructorThrowsWithEmptyQueryParameterValue(MediaTypeHeaderValue mediaType, string queryStringParameterValue) { Assert.ThrowsArgumentNull(() => new QueryStringMapping("query", queryStringParameterValue, mediaType), "queryStringParameterValue"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues")] public void ConstructorThrowsWithNullMediaTypeHeaderValue(string queryStringParameterName, string queryStringParameterValue) { Assert.ThrowsArgumentNull(() => new QueryStringMapping(queryStringParameterName, queryStringParameterValue, (MediaTypeHeaderValue)null), "mediaType"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void Constructor1(string queryStringParameterName, string queryStringParameterValue, string mediaType) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); Assert.Equal(queryStringParameterName, mapping.QueryStringParameterName); Assert.Equal(queryStringParameterValue, mapping.QueryStringParameterValue); Assert.MediaType.AreEqual(mediaType, mapping.MediaType, "MediaType failed to set."); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeStrings", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyQueryParameterName(string mediaType, string queryStringParameterName) { Assert.ThrowsArgumentNull(() => new QueryStringMapping(queryStringParameterName, "json", mediaType), "queryStringParameterName"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeStrings", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyQueryParameterValue(string mediaType, string queryStringParameterValue) { Assert.ThrowsArgumentNull(() => new QueryStringMapping("query", queryStringParameterValue, mediaType), "queryStringParameterValue"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyMediaType(string queryStringParameterName, string queryStringParameterValue, string mediaType) { GC.KeepAlive(mediaType); // Mark parameter as used. See xUnit1026, [Theory] method doesn't use all parameters. Assert.ThrowsArgumentNull(() => new QueryStringMapping(queryStringParameterName, queryStringParameterValue, (MediaTypeHeaderValue)null), "mediaType"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings", typeof(QueryStringMappingTests), "UriStringsWithoutQuery")] public void TryMatchMediaTypeReturnsMatchWithQueryStringParameterNameAndValueInUri(string queryStringParameterName, string queryStringParameterValue, string mediaType, string uriBase) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); string uri = uriBase + "?" + queryStringParameterName + "=" + queryStringParameterValue; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings", typeof(QueryStringMappingTests), "UriStringsWithoutQuery")] public void TryMatchMediaTypeReturnsZeroWithQueryStringParameterNameNotInUri(string queryStringParameterName, string queryStringParameterValue, string mediaType, string uriBase) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); string uri = uriBase + "?" + "not" + queryStringParameterName + "=" + queryStringParameterValue; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings", typeof(QueryStringMappingTests), "UriStringsWithoutQuery")] public void TryMatchMediaTypeReturnsZeroWithQueryStringParameterValueNotInUri(string queryStringParameterName, string queryStringParameterValue, string mediaType, string uriBase) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); string uri = uriBase + "?" + queryStringParameterName + "=" + "not" + queryStringParameterValue; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeThrowsWithNullHttpRequestMessage(string queryStringParameterName, string queryStringParameterValue, string mediaType) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); Assert.ThrowsArgumentNull(() => mapping.TryMatchMediaType(request: null), "request"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalQueryStringParameterNames", typeof(HttpTestData), "LegalQueryStringParameterValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeThrowsWithNullUriInHttpRequestMessage(string queryStringParameterName, string queryStringParameterValue, string mediaType) { QueryStringMapping mapping = new QueryStringMapping(queryStringParameterName, queryStringParameterValue, mediaType); string errorMessage = Error.Format(Properties.Resources.NonNullUriRequiredForMediaTypeMapping, typeof(QueryStringMapping).Name); Assert.Throws(() => mapping.TryMatchMediaType(new HttpRequestMessage()), errorMessage); } [Theory] [InlineData("nAmE", "VaLuE", "name=value")] [InlineData("Format", "Xml", "format=xml")] public void TryMatchMediaTypeIsCaseInsensitive(string name, string value, string query) { QueryStringMapping mapping = new QueryStringMapping(name, value, "application/json"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/?" + query); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/RequestHeaderMappingTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class RequestHeaderMappingTests { [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties( typeof(RequestHeaderMapping), TypeAssert.TypeProperties.IsPublicVisibleClass, typeof(MediaTypeMapping)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeHeaderValues")] public void Constructor(string headerName, string headerValue, MediaTypeHeaderValue mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.CurrentCulture, true, mediaType); Assert.Equal(headerName, mapping.HeaderName); Assert.Equal(headerValue, mapping.HeaderValue); Assert.Equal(StringComparison.CurrentCulture, mapping.HeaderValueComparison); Assert.True(mapping.IsValueSubstring); Assert.MediaType.AreEqual(mediaType, mapping.MediaType, "MediaType failed to set."); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeHeaderValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void ConstructorThrowsWithEmptyHeaderName(MediaTypeHeaderValue mediaType, string headerName) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping(headerName, "value", StringComparison.CurrentCulture, false, mediaType), "headerName"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeHeaderValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void ConstructorThrowsWithEmptyHeaderValue(MediaTypeHeaderValue mediaType, string headerValue) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping("name", headerValue, StringComparison.CurrentCulture, false, mediaType), "headerValue"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues")] public void ConstructorThrowsWithNullMediaTypeHeaderValue(string headerName, string headerValue) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping(headerName, headerValue, StringComparison.CurrentCulture, false, (MediaTypeHeaderValue)null), "mediaType"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeHeaderValues")] public void ConstructorThrowsWithInvalidStringComparison(string headerName, string headerValue, MediaTypeHeaderValue mediaType) { int invalidValue = 999; Assert.ThrowsInvalidEnumArgument(() => new RequestHeaderMapping(headerName, headerValue, (StringComparison)invalidValue, false, mediaType), "valueComparison", invalidValue, typeof(StringComparison)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void Constructor1(string headerName, string headerValue, string mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.CurrentCulture, true, mediaType); Assert.Equal(headerName, mapping.HeaderName); Assert.Equal(headerValue, mapping.HeaderValue); Assert.Equal(StringComparison.CurrentCulture, mapping.HeaderValueComparison); Assert.True(mapping.IsValueSubstring); Assert.MediaType.AreEqual(mediaType, mapping.MediaType, "MediaType failed to set."); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeStrings", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyHeaderName(string mediaType, string headerName) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping(headerName, "value", StringComparison.CurrentCulture, false, mediaType), "headerName"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalMediaTypeStrings", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyHeaderValue(string mediaType, string headerValue) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping("name", headerValue, StringComparison.CurrentCulture, false, mediaType), "headerValue"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(CommonUnitTestDataSets), "EmptyStrings")] public void Constructor1ThrowsWithEmptyMediaType(string headerName, string headerValue, string mediaType) { Assert.ThrowsArgumentNull(() => new RequestHeaderMapping(headerName, headerValue, StringComparison.CurrentCulture, false, mediaType), "mediaType"); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void Constructor1ThrowsWithInvalidStringComparison(string headerName, string headerValue, string mediaType) { int invalidValue = 999; Assert.ThrowsInvalidEnumArgument( () => new RequestHeaderMapping(headerName, headerValue, (StringComparison)invalidValue, false, mediaType), "valueComparison", invalidValue, typeof(StringComparison)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings", typeof(CommonUnitTestDataSets), "Bools")] public void TryMatchMediaTypeReturnsTrueWithNameAndValueInRequest(string headerName, string headerValue, string mediaType, bool subset) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.Ordinal, subset, mediaType); HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add(headerName, headerValue); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeReturnsTrueWithNameAndValueSubsetInRequest(string headerName, string headerValue, string mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.Ordinal, true, mediaType); HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add(headerName, "prefix" + headerValue); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add(headerName, headerValue + "postfix"); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add(headerName, "prefix" + headerValue + "postfix"); Assert.Equal(1.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeReturnsFalseWithNameNotInRequest(string headerName, string headerValue, string mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.Ordinal, false, mediaType); HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add("prefix" + headerName, headerValue); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add(headerName + "postfix", headerValue); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add("prefix" + headerName + "postfix", headerValue); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeReturnsFalseWithValueNotInRequest(string headerName, string headerValue, string mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.Ordinal, false, mediaType); HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add(headerName, "prefix" + headerValue); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add(headerName, headerValue + "postfix"); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); request = new HttpRequestMessage(); request.Headers.Add(headerName, "prefix" + headerValue + "postfix"); Assert.Equal(0.0, mapping.TryMatchMediaType(request)); } [Theory] [TestDataSet( typeof(HttpTestData), "LegalHttpHeaderNames", typeof(HttpTestData), "LegalHttpHeaderValues", typeof(HttpTestData), "LegalMediaTypeStrings")] public void TryMatchMediaTypeThrowsWithNullHttpRequestMessage(string headerName, string headerValue, string mediaType) { RequestHeaderMapping mapping = new RequestHeaderMapping(headerName, headerValue, StringComparison.CurrentCulture, true, mediaType); Assert.ThrowsArgumentNull(() => mapping.TryMatchMediaType(request: null), "request"); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/SerializerConsistencyTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { // Tests for ensuring the serializers behave consistently in various cases. // This is important for conneg. public class SerializerConsistencyTests { [Fact] public Task PartialContract() { var c = new PartialDataContract { PropertyWithAttribute = "one", #if !Testing_NetStandard1_3 // Xml formatter ignores DCS attributes but JSON one does not in netstandard1.3. PropertyWithoutAttribute = "false" #endif }; return SerializerConsistencyHepers.TestAsync(c); } [Fact] public Task ClassWithFields() { var c1 = new ClassWithFields { Property = "prop" }; c1.SetField("field"); return SerializerConsistencyHepers.TestAsync(c1); } [Fact] public Task PrivateProperty() { var source2 = new PrivateProperty { FirstName = "John", LastName = "Smith" }; source2.SetItem("shoes"); return SerializerConsistencyHepers.TestAsync(source2); } [Fact] public Task NormalClass() { var source = new NormalClass { FirstName = "John", LastName = "Smith", Item = "Socks" }; return SerializerConsistencyHepers.TestAsync(source); } [Fact] public Task InheritedProperties() { // Will we pick up inherited properties from a base object? BaseClass source = new DerivedClass { Property = "base", DerivedProperty = "derived" }; source.SetField("private"); return SerializerConsistencyHepers.TestAsync(source, typeof(DerivedClass)); } [Fact] public Task NullEmptyWhitespaceString() { NormalClass source = new NormalClass { FirstName = string.Empty, LastName = null, Item = " " }; return SerializerConsistencyHepers.TestAsync(source); } #if !Testing_NetStandard1_3 // XmlSerializer is unable to write XML for a dictionary. [Fact] public Task Dictionary() { var dict = new Dictionary(); dict["one"] = 1; dict["two"] = 2; return SerializerConsistencyHepers.TestAsync(dict); } #endif [Fact] public Task Array() { string[] array = new string[] { "First", "Second", "Last" }; return SerializerConsistencyHepers.TestAsync(array); } #if !Testing_NetStandard1_3 // XmlSerializer is unable to read XML for interfaces. [Fact] public async Task ArrayInterfaces() { string[] array = new string[] { "First", "Second", "Last" }; await SerializerConsistencyHepers.TestAsync(array, typeof(IList)); await SerializerConsistencyHepers.TestAsync(array, typeof(ICollection)); await SerializerConsistencyHepers.TestAsync(array, typeof(IEnumerable)); } [Fact] public Task Linq() { var l = from i in Enumerable.Range(1, 10) where i > 5 select i * i; // Runtime type of a linq expression is some derived Linq type which we can't deserialize to. // So explicitly call out IEnumerable return SerializerConsistencyHepers.TestAsync(l, typeof(IEnumerable)); } #endif [Fact] public Task StaticProps() { ClassWithStaticProperties source = new ClassWithStaticProperties(); return SerializerConsistencyHepers.TestAsync(source); } } // public class, public properties public class NormalClass { public string FirstName { get; set; } public string LastName { get; set; } public string Item { get; set; } } public class ClassWithStaticProperties { public string InstanceProp { get; set; } public static string StaticProp { get { Assert.True(false, "serializers should never call static properties"); return string.Empty; } set { Assert.True(false, "serializers should never call static properties"); throw new InvalidOperationException(); // assert already threw } } } [DataContract] public class PartialDataContract { [DataMember] public string PropertyWithAttribute { get; set; } #if !Testing_NetStandard1_3 // Xml formatter ignores DCS attributes but JSON one does not in netstandard1.3. // no attribute here public string PropertyWithoutAttribute { get; set; } #endif } public class PrivateProperty // with private field { public string FirstName { get; set; } public string LastName { get; set; } private string Item { get; set; } public void SetItem(string item) { this.Item = item; } } public class ClassWithFields { public string Property { get; set; } private string Field; public void SetField(string field) { this.Field = field; } } public class BaseClass { private string PrivateField; public string Property { get; set; } public void SetField(string field) { PrivateField = field; } } public class DerivedClass : BaseClass { public string DerivedProperty { get; set; } } // Helpers for performing consistency checks with the serializers. class SerializerConsistencyHepers { // Exercise the various serialization paths to verify that the default serializers behave consistently. public static Task TestAsync(object source) { Type tSource = source.GetType(); return TestAsync(source, tSource); } // Allow explicitly passing in the type that gets passed to the serializer. // The expectation is that the type can be read and written with both serializers. public static Task TestAsync(object source, Type tSource) { return TestAsync(source, tSource, tSource); } // tSourceWrite - the type we use for the initial write. This can be specific, and a 1-way serializable type (eg, a linq expression). // tSourceRead - the type that we read back as. This should be more general because we need to instantiate it. public static async Task TestAsync(object source, Type tSourceWrite, Type tSourceRead) { // Apply consistency chceks. This interleaves the results between the formatters. // It doesn't actually matter specifically what the formatter does, it just matters that they're consistent. // This will test various transitions between C#->JSON, JSON->C#, C#->XML, and XML->C#. // We can't compare C# objects, but we can compare the textual representation from XML and JSON. MediaTypeFormatter xmlFormatter = new MediaTypeFormatterCollection().XmlFormatter; MediaTypeFormatter jsonFor = new MediaTypeFormatterCollection().JsonFormatter; MemoryStream blobJson = await WriteAsync(source, tSourceWrite, jsonFor); // C# --> JSON MemoryStream blobXml = await WriteAsync(source, tSourceWrite, xmlFormatter); // C# --> XML object obj2 = await ReadAsync(blobJson, tSourceRead, jsonFor); // C# --> JSON --> C# object obj1 = await ReadAsync(blobXml, tSourceRead, xmlFormatter); // C# --> XML --> C# // We were able to round trip the source object through both formatters. // Now see if the resulting object is the same. // Check C# --> XML --> C# var blobXml2 = await WriteAsync(obj1, tSourceRead, xmlFormatter); // C# --> XML --> C# --> XML var blobJson2 = await WriteAsync(obj1, tSourceRead, jsonFor); // C# --> XML --> C# --> JSON // Ensure that C#->XMl and C#->XML->C#->XML give us the same result.. Compare(blobXml, blobXml2); // Ensure that C#->Json and C#->XML->C#->Json give us the same result Compare(blobJson, blobJson2); // Check C# --> JSON --> C# var blobXml3 = await WriteAsync(obj2, tSourceRead, xmlFormatter); // C# --> JSON --> C# --> XML var blobJson3 = await WriteAsync(obj2, tSourceRead, jsonFor); // C# --> JSON --> C# --> JSON // Ensure that C#->XML and C#->JSON->C#->XML are the same Compare(blobXml, blobXml3); // Ensure that C#->JSon and C#->JSON->C#->JSON are the same. Compare(blobJson, blobJson3); } // Compare if 2 streams have the same contents. private static void Compare(MemoryStream ms1, MemoryStream ms2) { string s1 = ToString(ms1); string s2 = ToString(ms2); Assert.Equal(s1, s2); } // Given a memory stream (which is representing a textual serialization format), get the string. private static string ToString(MemoryStream ms) { byte[] b = ms.GetBuffer(); return System.Text.Encoding.UTF8.GetString(b, 0, (int)ms.Length); } private static async Task ReadAsync(MemoryStream ms, Type tSource, MediaTypeFormatter formatter) { bool f = formatter.CanReadType(tSource); Assert.True(f); object o = await formatter.ReadFromStreamAsync(tSource, ms, content: null, formatterLogger: null); Assert.True(tSource.IsAssignableFrom(o.GetType())); return o; } private static async Task WriteAsync(object obj, Type tSource, MediaTypeFormatter formatter) { bool f = formatter.CanWriteType(tSource); Assert.True(f); MemoryStream ms = new MemoryStream(); await formatter.WriteToStreamAsync(tSource, obj, ms, content: null, transportContext: null); ms.Position = 0; return ms; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/StringComparisonHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class StringComparisonHelperTest : EnumHelperTestBase { public StringComparisonHelperTest() : base(StringComparisonHelper.IsDefined, StringComparisonHelper.Validate, (StringComparison)999) { } #if Testing_NetStandard1_3 // InvariantCulture and InvariantCultureIgnoreCase case are not supported in netstandard1.3 project protected override bool ValueExistsForFramework(StringComparison value) { return !(value == StringComparison.InvariantCulture || value == StringComparison.InvariantCultureIgnoreCase); } #endif } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/StringWithQualityHeaderValueComparerTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class StringWithQualityHeaderValueComparerTests { public static TheoryDataSet EqualValues { get { return new TheoryDataSet { { "value", "value" }, { "value", "VALUE" }, { "value", "value;q=1" }, { "value", "value; q=1" }, { "value", "value;q=1.0" }, { "value", "value; q=1.0" }, { "value", "value; q=1.00000" }, { "value; q=0.5", "value; q=0.5" }, { "value; q=1.0", "value; q=1.0" }, { "*", "*" }, { "*", "*;q=1" }, { "*", "*; q=1" }, { "*", "*;q=1.0" }, { "*", "*; q=1.0" }, { "*; q=0.5", "*; q=0.5" }, { "*; q=1.0", "*; q=1.0" }, { "value1", "value2" }, { "value1", "value2;q=1" }, { "value1", "value2; q=1" }, { "value1", "value2;q=1.0" }, { "value1", "value2; q=1.0" }, }; } } public static TheoryDataSet NonEqualValues { get { return new TheoryDataSet { { "value; q=0.5", "value" }, { "value; q=0.5", "value; q=1.0" }, { "value1; q=0.5", "value2; q=1.0" }, { "*", "value1" }, { "*;q=1", "value1" }, { "*; q=1", "value1" }, { "*;q=1.0", "value1" }, { "*; q=1.0", "value1" }, { "*; q=0.5", "value1; q=0.5" }, { "*; q=1.0", "value1; q=1.0" }, }; } } public static TheoryDataSet BeforeAfterSortedValues { get { return new TheoryDataSet { { new string[] { "text", "text;q=1.0", "text", "text;q=0", "*;q=0.8", "*;q=1", "text;q=0.8", "*;q=0.6", "text;q=1.0", "*;q=0.4", "text;q=0.6", }, new string[] { "text", "text;q=1.0", "text", "text;q=1.0", "*;q=1", "text;q=0.8", "*;q=0.8", "text;q=0.6", "*;q=0.6", "*;q=0.4", "text;q=0", } } }; } } [Fact] public void StaticComparerReturnsSameInstance() { StringWithQualityHeaderValueComparer comparer1 = StringWithQualityHeaderValueComparer.QualityComparer; StringWithQualityHeaderValueComparer comparer2 = StringWithQualityHeaderValueComparer.QualityComparer; Assert.NotNull(comparer1); Assert.Same(comparer1, comparer2); } [Theory] [PropertyData("EqualValues")] public void ComparerReturnsZeroForEqualValues(string stringWithQuality1, string stringWithQuality2) { // Arrange StringWithQualityHeaderValueComparer comparer = StringWithQualityHeaderValueComparer.QualityComparer; // Act StringWithQualityHeaderValue stringWithQualityHeaderValue1 = StringWithQualityHeaderValue.Parse(stringWithQuality1); StringWithQualityHeaderValue stringWithQualityHeaderValue2 = StringWithQualityHeaderValue.Parse(stringWithQuality2); // Assert Assert.Equal(0, comparer.Compare(stringWithQualityHeaderValue1, stringWithQualityHeaderValue2)); Assert.Equal(0, comparer.Compare(stringWithQualityHeaderValue2, stringWithQualityHeaderValue1)); } [Theory] [PropertyData("NonEqualValues")] public void ComparerReturnsNonZeroForNonEqualValues(string stringWithQuality1, string stringWithQuality2) { // Arrange StringWithQualityHeaderValueComparer comparer = StringWithQualityHeaderValueComparer.QualityComparer; // Act StringWithQualityHeaderValue stringWithQualityHeaderValue1 = StringWithQualityHeaderValue.Parse(stringWithQuality1); StringWithQualityHeaderValue stringWithQualityHeaderValue2 = StringWithQualityHeaderValue.Parse(stringWithQuality2); // Assert Assert.Equal(-1, comparer.Compare(stringWithQualityHeaderValue1, stringWithQualityHeaderValue2)); Assert.Equal(1, comparer.Compare(stringWithQualityHeaderValue2, stringWithQualityHeaderValue1)); } [Theory] [PropertyData("BeforeAfterSortedValues")] public void ComparerSortsListCorrectly(string[] unsorted, string[] expectedSorted) { // Arrange IEnumerable unsortedValues = unsorted.Select(u => StringWithQualityHeaderValue.Parse(u)); IEnumerable expectedSortedValues = expectedSorted.Select(u => StringWithQualityHeaderValue.Parse(u)); // Act IEnumerable actualSorted = unsortedValues.OrderByDescending(m => m, StringWithQualityHeaderValueComparer.QualityComparer); // Assert Assert.True(expectedSortedValues.SequenceEqual(actualSorted)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/XmlHttpRequestHeaderMappingTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class XmlHttpRequestHeaderMappingTest { // HttpRequestMessage request, double expectedMatch public static TheoryDataSet TryMatchMediaTypeData { get { return new TheoryDataSet() { { CreateXhrRequest(), 1.0 }, { CreateXhrRequest("*/*"), 1.0 }, { CreateXhrRequest("*/*; q=0.5"), 1.0 }, { CreateXhrRequest("text/*"), 0.0 }, { CreateXhrRequest("text/*; q=0.5"), 0.0 }, { CreateXhrRequest("application/xml"), 0.0 }, { CreateXhrRequest("application/xml; q=0.5"), 0.0 }, { CreateXhrRequest("text/test", "*/*; q=0.5"), 0.0 }, }; } } private static HttpRequestMessage CreateXhrRequest(params string[] acceptHeaders) { HttpRequestMessage request = new HttpRequestMessage(); request.Headers.Add("X-Requested-With", "XmlHttpRequest"); foreach (string accept in acceptHeaders) { request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(accept)); } return request; } [Fact] public void Constructor_Initializes() { XmlHttpRequestHeaderMapping mapping = new XmlHttpRequestHeaderMapping(); Assert.Equal("x-requested-with", mapping.HeaderName); Assert.Equal("XMLHttpRequest", mapping.HeaderValue); Assert.Equal(StringComparison.OrdinalIgnoreCase, mapping.HeaderValueComparison); Assert.True(mapping.IsValueSubstring); Assert.Equal(MediaTypeConstants.ApplicationJsonMediaType, mapping.MediaType); } [Fact] public void TryMatchMediaType_ThrowsOnNull() { XmlHttpRequestHeaderMapping mapping = new XmlHttpRequestHeaderMapping(); Assert.ThrowsArgumentNull(() => mapping.TryMatchMediaType(null), "request"); } [Theory] [PropertyData("TryMatchMediaTypeData")] public void TryMatchMediaType_Matches(HttpRequestMessage request, double expectedMatch) { // Arrange XmlHttpRequestHeaderMapping mapping = new XmlHttpRequestHeaderMapping(); // Act double actualMatch = mapping.TryMatchMediaType(request); // Assert Assert.Equal(expectedMatch, actualMatch); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/XmlMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; using Microsoft.TestCommon; using Moq; using Newtonsoft.Json; namespace System.Net.Http.Formatting { public class XmlMediaTypeFormatterTests : MediaTypeFormatterTestBase { // Test data which should round-trip using a media type that includes type information. A representative // sample only; avoids types DataContractJsonSerializer fails to round trip (e.g. Guid, Uint16). May require // known types or similar (de)serializer configuration. public static readonly RefTypeTestData BunchOfTypedObjectsTestData = new RefTypeTestData( () => new List { null, String.Empty, "This is a string", false, true, Double.MinValue, Double.MaxValue, Int32.MinValue, Int32.MaxValue, Int64.MinValue, Int64.MaxValue, #if !NETCOREAPP2_1 // DBNull not serializable on .NET Core 2.1. DBNull.Value, #endif }); public static readonly TheoryDataSet AFewValidTypes = new() { typeof(bool), typeof(int), typeof(string), }; public static IEnumerable BunchOfTypedObjectsTestDataCollection { get { return new TestData[] { BunchOfTypedObjectsTestData, }; } } public override IEnumerable ExpectedSupportedMediaTypes { get { return HttpTestData.StandardXmlMediaTypes; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return ExpectedSupportedEncodings.ElementAt(0).GetBytes("42"); } } [Fact] void CopyConstructor() { TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter() { Indent = true, MaxDepth = 42, UseXmlSerializer = true }; TestXmlMediaTypeFormatter derivedFormatter = new TestXmlMediaTypeFormatter(formatter); Assert.Equal(formatter.Indent, derivedFormatter.Indent); Assert.Equal(formatter.MaxDepth, derivedFormatter.MaxDepth); Assert.Equal(formatter.UseXmlSerializer, derivedFormatter.UseXmlSerializer); } [Fact] public void DefaultMediaType_ReturnsApplicationXml() { MediaTypeHeaderValue mediaType = XmlMediaTypeFormatter.DefaultMediaType; Assert.NotNull(mediaType); Assert.Equal("application/xml", mediaType.MediaType); } [Fact] public void MaxDepthReturnsCorrectValue() { Assert.Reflection.IntegerProperty( new XmlMediaTypeFormatter(), f => f.MaxDepth, expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, illegalUpperValue: null, roundTripTestValue: 10); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public async Task ReadDeeplyNestedObjectThrows() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter() { MaxDepth = 1 }; MemoryStream stream = new MemoryStream(); await formatter.WriteToStreamAsync(typeof(SampleType), new SampleType() { Number = 1 }, stream, null, null); stream.Position = 0; await Assert.ThrowsAsync(() => formatter.ReadFromStreamAsync(typeof(SampleType), stream, null, null)); } #endif [Fact] public void Indent_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlMediaTypeFormatter(), c => c.Indent, expectedDefaultValue: false); } [Fact] public void UseXmlSerializer_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlMediaTypeFormatter(), c => c.UseXmlSerializer, expectedDefaultValue: false); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Theory] [InlineData(typeof(IEnumerable))] [InlineData(typeof(IQueryable))] public async Task UseXmlFormatterWithNull(Type type) { XmlMediaTypeFormatter xmlFormatter = new XmlMediaTypeFormatter { UseXmlSerializer = false }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(type, null, memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("nil=\"true\""), "Null value should be serialized as nil."); Assert.True(serializedString.ToLower().Contains("arrayofstring"), "It should be serialized out as an array of string."); } [Fact] public async Task UseXmlSerializer_False() { XmlMediaTypeFormatter xmlFormatter = new XmlMediaTypeFormatter { UseXmlSerializer = false }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("DataContractSampleType"), "SampleType should be serialized with data contract name DataContractSampleType because we're using DCS."); Assert.False(serializedString.Contains("version=\"1.0\" encoding=\"utf-8\""), "Using DCS should not emit the xml declaration by default."); Assert.False(serializedString.Contains("\r\n"), "Using DCS should emit data without indentation by default."); } [Fact] public async Task UseXmlSerializer_False_Indent() { XmlMediaTypeFormatter xmlFormatter = new XmlMediaTypeFormatter { UseXmlSerializer = false, Indent = true }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("\r\n"), "Using DCS with indent set to true should emit data with indentation."); } #endif [Fact] public void SetSerializer_ThrowsWithNullType() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); XmlSerializer xmlSerializer = new XmlSerializer(typeof(string)); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(null, xmlSerializer); }, "type"); } [Fact] public void SetSerializer_ThrowsWithNullSerializer() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(typeof(string), (XmlSerializer)null); }, "serializer"); } [Fact] public void SetSerializer1_ThrowsWithNullSerializer() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer((XmlSerializer)null); }, "serializer"); } [Fact] public void SetSerializer2_ThrowsWithNullType() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); XmlObjectSerializer xmlObjectSerializer = new DataContractSerializer(typeof(string)); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(null, xmlObjectSerializer); }, "type"); } [Fact] public void SetSerializer2_ThrowsWithNullSerializer() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(typeof(string), (XmlObjectSerializer)null); }, "serializer"); } [Fact] public void SetSerializer3_ThrowsWithNullSerializer() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer((XmlSerializer)null); }, "serializer"); } [Fact] public void RemoveSerializer_ThrowsWithNullType() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.RemoveSerializer(null); }, "type"); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateFails() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerXmlSerializer); } [Fact] public async Task FormatterThrowsOnWriteWhenOverridenCreateReturnsNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerXmlSerializer); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateFails() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerXmlSerializer); } [Fact] public async Task FormatterThrowsOnReadWhenOverridenCreateReturnsNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action); Assert.NotNull(formatter.InnerDataContractSerializer); Assert.Null(formatter.InnerXmlSerializer); } #endif [Fact] public async Task XmlSerializerFormatterThrowsOnWriteWhenOverridenCreateFails() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; formatter.UseXmlSerializer = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerXmlSerializer); } [Fact] public async Task XmlSerializerFormatterThrowsOnWriteWhenOverridenCreateReturnsNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; formatter.UseXmlSerializer = true; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); // Act & Assert Func action = () => formatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null); await Assert.ThrowsAsync(action); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerXmlSerializer); } [Fact] public async Task XmlSerializerFormatterThrowsOnReadWhenOverridenCreateFails() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ThrowAnExceptionOnCreate = true; formatter.UseXmlSerializer = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerXmlSerializer); } [Fact] public async Task XmlSerializerFormatterThrowsOnReadWhenOverridenCreateReturnsNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.ReturnNullOnCreate = true; formatter.UseXmlSerializer = true; byte[] array = Encoding.UTF8.GetBytes("foo"); MemoryStream memoryStream = new MemoryStream(array); HttpContent content = new StringContent("foo"); // Act & Assert Func action = () => formatter.ReadFromStreamAsync(typeof(SampleType), memoryStream, content, null); await Assert.ThrowsAsync(action); Assert.Null(formatter.InnerDataContractSerializer); Assert.NotNull(formatter.InnerXmlSerializer); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithXmlSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.SetSerializer(variationType, new XmlSerializer(variationType)); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } [Theory] [TestDataSet(typeof(XmlMediaTypeFormatterTests), "BunchOfTypedObjectsTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer_ExtraTypes(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithXmlSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.SetSerializer(variationType, new XmlSerializer(variationType, new Type[] { typeof(DBNull), })); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } // Test alternate null value; this serializer attempts to cast DBNull to variationType so typeof(string) variation fails [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer_DBNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); Type variationType = typeof(DBNull); formatter.SetSerializer(variationType, new XmlSerializer(variationType)); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStream_AsyncRoundTripsWriteToStreamUsingDataContractSerializer(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithDataContractSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.SetSerializer(variationType, new DataContractSerializer(variationType)); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } [Theory] [TestDataSet(typeof(XmlMediaTypeFormatterTests), "BunchOfTypedObjectsTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStream_AsyncRoundTripsWriteToStreamUsingDataContractSerializer_KnownTypes(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithDataContractSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); formatter.SetSerializer(variationType, new DataContractSerializer(variationType, new Type[] { typeof(DBNull), })); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } #if Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_UsingDataContractSerializer_Throws(Type variationType, object testData) { // Arrange. First, get some data using XmlSerializer. bool canSerialize = IsSerializableWithXmlSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { var formatter = new XmlMediaTypeFormatter() { UseXmlSerializer = true }; using var stream = new MemoryStream(); using var content = new StringContent(string.Empty); await formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null); await stream.FlushAsync(); stream.Position = 0L; content.Headers.ContentLength = stream.Length; formatter.RemoveSerializer(variationType); formatter.UseXmlSerializer = false; // Act & Assert await Assert.ThrowsAsync(() => formatter.ReadFromStreamAsync(variationType, stream, content, formatterLogger: null), "Unable to validate types on this platform when UseXmlSerializer is 'false'. Please set " + "UseXmlSerializer or move to a supported platform, one where the .NET Standard 2.0 assembly " + "is usable."); } } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task WriteToStreamAsync_UsingDataContractSerializer_Throws(Type variationType, object testData) { // Arrange var formatter = new XmlMediaTypeFormatter(); using var stream = new MemoryStream(); using var content = new StringContent(string.Empty); // Act & Assert await Assert.ThrowsAsync(() => formatter.WriteToStreamAsync(variationType, testData, stream, content, transportContext: null), "Unable to validate types on this platform when UseXmlSerializer is 'false'. Please set " + "UseXmlSerializer or move to a supported platform, one where the .NET Standard 2.0 assembly " + "is usable."); } #else #if !NETCOREAPP2_1 // DBNull not serializable on .NET Core 2.1. [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingDataContractSerializer_DBNull() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); Type variationType = typeof(DBNull); formatter.SetSerializer(variationType, new DataContractSerializer(variationType)); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // DBNull.Value round-trips as either Object or DBNull because serialization includes its type Assert.Equal(testData, readObj); } [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingDataContractSerializer_DBNullAsEmptyString() { // Arrange TestXmlMediaTypeFormatter formatter = new TestXmlMediaTypeFormatter(); Type variationType = typeof(string); formatter.SetSerializer(variationType, new DataContractSerializer(variationType, new Type[] { typeof(DBNull), })); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); // Lower levels convert DBNull.Value to empty string on read Assert.Equal(String.Empty, readObj); } #endif public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); string formattedContent = "" + content + ""; string mediaType = string.Format("application/xml; charset={0}", encoding); // Act & assert return ReadFromStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } #endif [Fact] public async Task ReadFromStreamAsync_UsesGetDeserializerAndCreateXmlReader() { Type type = typeof(string); string xml = "x"; Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); var serializer = new Mock() { CallBase = true }; var reader = new Mock() { CallBase = true }; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetDeserializer(type, null)).Returns(serializer.Object); formatter.Setup(f => f.CreateXmlReader(stream, null)).Returns(reader.Object); await formatter.Object.ReadFromStreamAsync(type, stream, content: null, formatterLogger: null); serializer.Verify(s => s.ReadObject(reader.Object)); } [Fact] public Task ReadFromStreamAsync_ThrowsException_WhenGetDeserializerReturnsNull() { Type type = typeof(string); string xml = "x"; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetDeserializer(type, null)).Returns(null); return Assert.ThrowsAsync( () => formatter.Object.ReadFromStreamAsync(type, new MemoryStream(Encoding.UTF8.GetBytes(xml)), content: null, formatterLogger: null), "The object returned by GetDeserializer must not be a null value."); } [Fact] public Task ReadFromStreamAsync_ThrowsException_WhenGetDeserializerReturnsInvalidType() { Type type = typeof(string); string xml = "x"; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetDeserializer(type, null)).Returns(new JsonSerializer()); return Assert.ThrowsAsync( () => formatter.Object.ReadFromStreamAsync(type, new MemoryStream(Encoding.UTF8.GetBytes(xml)), content: null, formatterLogger: null), "The object of type 'JsonSerializer' returned by GetDeserializer must be an instance of either XmlObjectSerializer or XmlSerializer."); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); string formattedContent = "" + content + ""; string mediaType = string.Format("application/xml; charset={0}", encoding); // Act & assert return WriteToStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } #endif [Fact] public async Task WriteToStreamAsync_UsesGetSerializerAndCreateXmlWriter() { Type type = typeof(string); object value = "x"; Stream stream = new MemoryStream(); var serializer = new Mock() { CallBase = true }; var writer = new Mock() { CallBase = true }; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetSerializer(type, value, null)).Returns(serializer.Object); formatter.Setup(f => f.CreateXmlWriter(stream, null)).Returns(writer.Object); await formatter.Object.WriteToStreamAsync(type, value, stream, content: null, transportContext: null); serializer.Verify(s => s.WriteObject(writer.Object, value)); } [Fact] public Task WriteToStreamAsync_ThrowsException_WhenGetSerializerReturnsNull() { Type type = typeof(string); object value = "x"; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetSerializer(type, value, null)).Returns(null); return Assert.ThrowsAsync( () => formatter.Object.WriteToStreamAsync(type, value, new MemoryStream(), content: null, transportContext: null), "The object returned by GetSerializer must not be a null value."); } [Fact] public Task WriteToStreamAsync_ThrowsException_WhenGetSerializerReturnsInvalidType() { Type type = typeof(string); object value = "x"; var formatter = new Mock() { CallBase = true }; formatter.Setup(f => f.GetSerializer(type, value, null)).Returns(new JsonSerializer()); return Assert.ThrowsAsync( () => formatter.Object.WriteToStreamAsync(type, value, new MemoryStream(), content: null, transportContext: null), "The object of type 'JsonSerializer' returned by GetSerializer must be an instance of either XmlObjectSerializer or XmlSerializer."); } [Fact] public void CreateXmlWriter_Uses_WriterSettings() { // Arrange XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); formatter.WriterSettings.ConformanceLevel = ConformanceLevel.Fragment; Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); // Act XmlWriter writer = formatter.CreateXmlWriter(stream, content); // Assert Assert.Equal(writer.Settings.ConformanceLevel, formatter.WriterSettings.ConformanceLevel); } [Fact] public void Property_WriterSettings_DefaultValues() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.NotNull(formatter.WriterSettings); Assert.False(formatter.WriterSettings.Indent); Assert.False(formatter.WriterSettings.CloseOutput); Assert.True(formatter.WriterSettings.OmitXmlDeclaration); Assert.False(formatter.WriterSettings.CheckCharacters); } #if !Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public async Task InvalidXmlCharacters_CanBeSerialized_Default() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); await formatter.WriteToStreamAsync(typeof(string), "\x16", stream, content, null); } [Fact] public Task InvalidXmlCharacters_CannotBeSerialized_IfCheckCharactersIsTrue() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); formatter.WriterSettings.CheckCharacters = true; Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); return Assert.ThrowsAsync( () => formatter.WriteToStreamAsync(typeof(string), "\x16", stream, content, null), "'\x16', hexadecimal value 0x16, is an invalid character."); } #endif [Theory] [PropertyData(nameof(AFewValidTypes))] public void CanReadType_ReturnsFalse_ForValidTypes(Type type) { XmlMediaTypeFormatter formatter = new(); var canRead = formatter.CanReadType(type); #if Testing_NetStandard1_3 // Different behavior in netstandard1.3 due to no DataContract validation. Assert.False(canRead); #else Assert.True(canRead); #endif } [Theory] [PropertyData(nameof(AFewValidTypes))] public void CanWriteType_ReturnsFalse_ForValidTypes(Type type) { XmlMediaTypeFormatter formatter = new(); var canWrite = formatter.CanWriteType(type); #if Testing_NetStandard1_3 // Different behavior in netstandard1.3 due to no DataContract validation. Assert.False(canWrite); #else Assert.True(canWrite); #endif } [Fact] public void CanReadType_ReturnsFalse_ForInvalidDataContracts() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.False(formatter.CanReadType(typeof(InvalidDataContract))); } [Fact] public void CanWriteType_ReturnsFalse_ForInvalidDataContracts() { XmlMediaTypeFormatter formatter = new XmlMediaTypeFormatter(); Assert.False(formatter.CanWriteType(typeof(InvalidDataContract))); } #if Testing_NetStandard1_3 // Cannot read or write w/ DCS in netstandard1.3. [Fact] public override Task Overridden_ReadFromStreamAsyncWithCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_ReadFromStreamAsyncWithoutCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_WriteToStreamAsyncWithCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task Overridden_WriteToStreamAsyncWithoutCancellationToken_GetsCalled() { return Task.CompletedTask; } [Fact] public override Task ReadFromStreamAsync_ReadsDataButDoesNotCloseStream() { return Task.CompletedTask; } // Attributes are in base class. public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { return Task.CompletedTask; } [Fact] public override Task ReadFromStreamAsync_WhenContentLengthIsNull_ReadsDataButDoesNotCloseStream() { return Task.CompletedTask; } // Attributes are in base class. public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { return Task.CompletedTask; } [Fact] public override Task WriteToStreamAsync_WhenObjectIsNull_WritesDataButDoesNotCloseStream() { return Task.CompletedTask; } [Fact] public override Task WriteToStreamAsync_WritesDataButDoesNotCloseStream() { return Task.CompletedTask; } #endif public class InvalidDataContract { // removing the default ctor makes this invalid public InvalidDataContract(string s) { } } public class TestXmlMediaTypeFormatter : XmlMediaTypeFormatter { public TestXmlMediaTypeFormatter() { } public TestXmlMediaTypeFormatter(TestXmlMediaTypeFormatter formatter) : base(formatter) { } public bool ThrowAnExceptionOnCreate { get; set; } public bool ReturnNullOnCreate { get; set; } public XmlSerializer InnerXmlSerializer { get; private set; } public DataContractSerializer InnerDataContractSerializer { get; private set; } public bool CanReadTypeCaller(Type type) { return CanReadType(type); } public bool CanWriteTypeCaller(Type type) { return CanWriteType(type); } public override XmlSerializer CreateXmlSerializer(Type type) { InnerXmlSerializer = base.CreateXmlSerializer(type); if (ReturnNullOnCreate) { return null; } if (ThrowAnExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return InnerXmlSerializer; } public override DataContractSerializer CreateDataContractSerializer(Type type) { InnerDataContractSerializer = base.CreateDataContractSerializer(type); if (ReturnNullOnCreate) { return null; } if (ThrowAnExceptionOnCreate) { throw new Exception("Throwing exception directly, since it needs to get caught by a catch all"); } return InnerDataContractSerializer; } } private bool IsSerializableWithXmlSerializer(Type type, object obj) { if (Assert.Http.IsKnownUnserializable(type, obj)) { return false; } try { new XmlSerializer(type); if (obj != null && obj.GetType() != type) { new XmlSerializer(obj.GetType()); } } catch { return false; } return true; } private bool IsSerializableWithDataContractSerializer(Type type, object obj) { #if Testing_NetStandard1_3 // Different behavior in netstandard1.3 due to no DataContract validation. return false; #else if (Assert.Http.IsKnownUnserializable(type, obj)) { return false; } try { new DataContractSerializer(type); if (obj != null && obj.GetType() != type) { new DataContractSerializer(obj.GetType()); } } catch { return false; } return true; #endif } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Formatting/XmlSerializerMediaTypeFormatterTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.DataSets; using System.Net.Http.Headers; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; using Microsoft.TestCommon; namespace System.Net.Http.Formatting { public class XmlSerializerMediaTypeFormatter : XmlMediaTypeFormatter { public XmlSerializerMediaTypeFormatter() { UseXmlSerializer = true; } } public class XmlSerializerMediaTypeFormatterTests : MediaTypeFormatterTestBase { public override IEnumerable ExpectedSupportedMediaTypes { get { return HttpTestData.StandardXmlMediaTypes; } } public override IEnumerable ExpectedSupportedEncodings { get { return HttpTestData.StandardEncodings; } } public override byte[] ExpectedSampleTypeByteRepresentation { get { return ExpectedSupportedEncodings.ElementAt(0).GetBytes("42"); } } [Fact] public void DefaultMediaType_ReturnsApplicationXml() { MediaTypeHeaderValue mediaType = XmlSerializerMediaTypeFormatter.DefaultMediaType; Assert.NotNull(mediaType); Assert.Equal("application/xml", mediaType.MediaType); } [Fact] public void Indent_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlSerializerMediaTypeFormatter(), c => c.Indent, expectedDefaultValue: false); } [Fact] public async Task ReadDeeplyNestedObjectWorks() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter() { MaxDepth = 5001 }; StringContent content = new StringContent(GetDeeplyNestedObject(5000)); content.Headers.ContentType = new MediaTypeHeaderValue("application/xml"); Assert.IsType(await formatter.ReadFromStreamAsync(typeof(Nest), await content.ReadAsStreamAsync(), content, null)); } [Fact] public void UseXmlSerializer_RoundTrips() { Assert.Reflection.BooleanProperty( new XmlSerializerMediaTypeFormatter(), c => c.UseXmlSerializer, expectedDefaultValue: true); } [Theory] [InlineData(typeof(IQueryable))] [InlineData(typeof(IEnumerable))] public async Task UseXmlFormatterWithNull(Type type) { XmlMediaTypeFormatter xmlFormatter = new XmlSerializerMediaTypeFormatter(); MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(type, null, memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("nil=\"true\""), "Null value should be serialized as nil."); Assert.True(serializedString.ToLower().Contains("arrayofstring"), "It should be serialized out as an array of string."); } [Fact] public async Task UseXmlSerializer_True() { XmlSerializerMediaTypeFormatter xmlFormatter = new XmlSerializerMediaTypeFormatter(); MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.False(serializedString.Contains("DataContractSampleType"), "SampleType should not be serialized with data contract name DataContractSampleType because UseXmlSerializer is set to true."); Assert.False(serializedString.Contains("version=\"1.0\" encoding=\"utf-8\""), "Using XmlSerializer should not emit the xml declaration by default."); Assert.False(serializedString.Contains("\r\n"), "Using default XmlSerializer should emit data without indentation."); } [Fact] public async Task UseXmlSerializer_True_Indent() { XmlSerializerMediaTypeFormatter xmlFormatter = new XmlSerializerMediaTypeFormatter { Indent = true }; MemoryStream memoryStream = new MemoryStream(); HttpContent content = new StringContent(String.Empty); await Assert.Task.SucceedsAsync(xmlFormatter.WriteToStreamAsync(typeof(SampleType), new SampleType(), memoryStream, content, transportContext: null)); memoryStream.Position = 0; string serializedString = new StreamReader(memoryStream).ReadToEnd(); Assert.True(serializedString.Contains("\r\n"), "Using default XmlSerializer with Indent set to true should emit data with indentation."); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection")] public void CanReadType_ReturnsSameResultAsXmlSerializerConstructor(Type variationType, object testData) { TestXmlSerializerMediaTypeFormatter formatter = new TestXmlSerializerMediaTypeFormatter(); bool isSerializable = IsSerializableWithXmlSerializer(variationType, testData); bool canSupport = formatter.CanReadTypeCaller(variationType); if (isSerializable != canSupport) { Assert.Equal(isSerializable, canSupport); } // Ask a 2nd time to probe whether the cached result is treated the same canSupport = formatter.CanReadTypeCaller(variationType); Assert.Equal(isSerializable, canSupport); } [Fact] public void SetSerializer_ThrowsWithNullType() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); XmlSerializer xmlSerializer = new XmlSerializer(typeof(string)); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(null, xmlSerializer); }, "type"); } [Fact] public void SetSerializer_ThrowsWithNullSerializer() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(typeof(string), (XmlSerializer)null); }, "serializer"); } [Fact] public void SetSerializer1_ThrowsWithNullSerializer() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer((XmlSerializer)null); }, "serializer"); } [Fact] public void SetSerializer2_ThrowsWithNullType() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); XmlObjectSerializer xmlObjectSerializer = new DataContractSerializer(typeof(string)); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(null, xmlObjectSerializer); }, "type"); } [Fact] public void SetSerializer2_ThrowsWithNullSerializer() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer(typeof(string), (XmlObjectSerializer)null); }, "serializer"); } [Fact] public void SetSerializer3_ThrowsWithNullSerializer() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.SetSerializer((XmlSerializer)null); }, "serializer"); } [Fact] public void RemoveSerializer_ThrowsWithNullType() { XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); Assert.ThrowsArgumentNull(() => { formatter.RemoveSerializer(null); }, "type"); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "RepresentativeValueAndRefTypeTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithXmlSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlSerializerMediaTypeFormatter formatter = new TestXmlSerializerMediaTypeFormatter(); formatter.SetSerializer(variationType, new XmlSerializer(variationType)); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } [Theory] [TestDataSet(typeof(XmlMediaTypeFormatterTests), "BunchOfTypedObjectsTestDataCollection", RoundTripDataVariations)] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer_ExtraTypes(Type variationType, object testData) { // Guard bool canSerialize = IsSerializableWithXmlSerializer(variationType, testData) && Assert.Http.CanRoundTrip(variationType); if (canSerialize) { // Arrange TestXmlSerializerMediaTypeFormatter formatter = new TestXmlSerializerMediaTypeFormatter(); formatter.SetSerializer(variationType, new XmlSerializer(variationType, new Type[] { typeof(DBNull), })); // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } } // Test alternate null value; this serializer attempts to cast DBNull to variationType so typeof(string) variation fails [Fact] public async Task ReadFromStreamAsync_RoundTripsWriteToStreamAsyncUsingXmlSerializer_DBNull() { // Arrange TestXmlSerializerMediaTypeFormatter formatter = new TestXmlSerializerMediaTypeFormatter(); Type variationType = typeof(DBNull); formatter.SetSerializer(variationType, new XmlSerializer(variationType)); object testData = DBNull.Value; // Arrange & Act & Assert object readObj = await ReadFromStreamAsync_RoundTripsWriteToStreamAsync_Helper(formatter, variationType, testData); Assert.Equal(testData, readObj); } public override Task ReadFromStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); string formattedContent = "" + content + ""; string mediaType = string.Format("application/xml; charset={0}", encoding); // Act & assert return ReadFromStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } public override Task WriteToStreamAsync_UsesCorrectCharacterEncoding(string content, string encoding, bool isDefaultEncoding) { // Arrange XmlSerializerMediaTypeFormatter formatter = new XmlSerializerMediaTypeFormatter(); string formattedContent = "" + content + ""; string mediaType = string.Format("application/xml; charset={0}", encoding); // Act & assert return WriteToStreamAsync_UsesCorrectCharacterEncodingHelper(formatter, content, formattedContent, mediaType, encoding, isDefaultEncoding); } static string GetDeeplyNestedObject(int depth) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < depth; i++) { sb.Insert(0, ""); sb.Append(""); } sb.Insert(0, ""); sb.Append(""); sb.Insert(0, ""); return sb.ToString(); } public class TestXmlSerializerMediaTypeFormatter : XmlSerializerMediaTypeFormatter { public bool CanReadTypeCaller(Type type) { return CanReadType(type); } public bool CanWriteTypeCaller(Type type) { return CanWriteType(type); } } [XmlRoot("Nest", Namespace = "http://example.com")] public class Nest { public Nest A { get; set; } } private bool IsSerializableWithXmlSerializer(Type type, object obj) { if (Assert.Http.IsKnownUnserializable(type, obj)) { return false; } try { new XmlSerializer(type); if (obj != null && obj.GetType() != type) { new XmlSerializer(obj.GetType()); } } catch { return false; } return true; } private bool IsSerializableWithDataContractSerializer(Type type, object obj) { if (Assert.Http.IsKnownUnserializable(type, obj)) { return false; } try { new DataContractSerializer(type); if (obj != null && obj.GetType() != type) { new DataContractSerializer(obj.GetType()); } } catch { return false; } return true; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/FormattingUtilitiesTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Net.Http.Headers; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http { public class FormattingUtilitiesTests { public static TheoryDataSet QuotedStrings { get { return new TheoryDataSet() { { @"""""", @"" }, { @"""string""", @"string" }, { @"string", @"string" }, { @"""str""ing""", @"str""ing" }, }; } } public static TheoryDataSet NotQuotedStrings { get { return new TheoryDataSet { @" """, @" """"", @"string", @"str""ing", @"s""trin""g", }; } } public static TheoryDataSet ValidHeaderTokens { get { return new TheoryDataSet { Convert.ToChar(0x21).ToString(), Convert.ToChar(0x7E).ToString(), "acb", "ABC", "a&b", }; } } public static TheoryDataSet InvalidHeaderTokens { get { return new TheoryDataSet { Convert.ToChar(0x20).ToString(), Convert.ToChar(0x7F).ToString(), null, "", "[ABC]", "{a&b}", "æææøøøååå", "いくつかのテキスト", }; } } public static TheoryDataSet ValidDateStringValues { get { return new TheoryDataSet { { new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero), "Sun, 06 Nov 1994 08:49:37 GMT" }, }; } } public static TheoryDataSet ValidDateValues { get { return new TheoryDataSet { // RFC1123 date/time value { "Sun, 06 Nov 1994 08:49:37 GMT", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "Sun, 06 Nov 1994 08:49:37", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "6 Nov 1994 8:49:37 GMT", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "6 Nov 1994 8:49:37", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "Sun, 06 Nov 94 08:49:37", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "6 Nov 94 8:49:37", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, // RFC850 date/time value { "Sunday, 06-Nov-94 08:49:37 GMT", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "Sunday, 6-Nov-94 8:49:37", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "Sun, 6-Nov-1994 8:49:37 GMT", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, // ANSI C's asctime() format { "Sun Nov 06 08:49:37 1994", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, { "Sun Nov 6 8:49:37 1994", new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero) }, // RFC5322 date/time { "Sat, 08 Nov 1997 09:55:06 -0600", new DateTimeOffset(1997, 11, 8, 9, 55, 6, new TimeSpan(-6, 0, 0)) }, { "8 Nov 1997 9:55:6", new DateTimeOffset(1997, 11, 8, 9, 55, 6, TimeSpan.Zero) }, { "Sat, 8 Nov 1997 9:55:6 +0200", new DateTimeOffset(1997, 11, 8, 9, 55, 6, new TimeSpan(2, 0, 0)) }, }; } } public static TheoryDataSet InvalidDateValues { get { return new TheoryDataSet { "Sun, 06 Nov 1994 08:49:37 GMT invalid", "Sun, 06 Nov 1994 08:49:37 GMT,", ",Sun, 06 Nov 1994 08:49:37 GMT", "Sun, 06 Nov 1994 08:49:37 æøå", "Sun, 06 æøå 1994 08:49:37 GMT", "Sun, 06 Nov 1994 08:49:37 いくつ", "Sun, 06 いくつ 1994 08:49:37 Nov", "æææøøøååå", "いくつかのテキスト", }; } } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(FormattingUtilities), TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsStatic); } [Fact] public void IsJsonValueTypeReturnsTrue() { Assert.True(FormattingUtilities.IsJTokenType(typeof(JToken)), "Should return true"); Assert.True(FormattingUtilities.IsJTokenType(typeof(JValue)), "Should return true"); Assert.True(FormattingUtilities.IsJTokenType(typeof(JObject)), "Should return true"); Assert.True(FormattingUtilities.IsJTokenType(typeof(JArray)), "Should return true"); } [Fact] public void CreateEmptyContentHeadersReturnsEmptyHeaders() { HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); Assert.NotNull(headers); Assert.Empty(headers); } [Theory] [TestDataSet(typeof(CommonUnitTestDataSets), "EmptyStrings")] public void UnquoteTokenReturnsSameRefOnEmpty(string empty) { string result = FormattingUtilities.UnquoteToken(empty); Assert.Same(empty, result); } [Theory] [PropertyData("NotQuotedStrings")] public void UnquoteTokenReturnsSameRefNonQuotedStrings(string test) { string result = FormattingUtilities.UnquoteToken(test); Assert.Equal(test, result); } [Theory] [PropertyData("QuotedStrings")] public void UnquoteTokenReturnsUnquotedStrings(string token, string expectedResult) { string result = FormattingUtilities.UnquoteToken(token); Assert.Equal(expectedResult, result); } [Theory] [PropertyData("ValidHeaderTokens")] public void ValidateHeaderToken_AcceptsValidTokens(string validToken) { bool result = FormattingUtilities.ValidateHeaderToken(validToken); Assert.True(result); } [Theory] [PropertyData("InvalidHeaderTokens")] public void ValidateHeaderToken_RejectsInvalidTokens(string invalidToken) { bool result = FormattingUtilities.ValidateHeaderToken(invalidToken); Assert.False(result); } [Theory] [PropertyData("ValidDateStringValues")] public void DateToString_GeneratesValidValue(DateTimeOffset input, string expectedValue) { string actualValue = FormattingUtilities.DateToString(input); Assert.Equal(expectedValue, actualValue); } [Theory] [PropertyData("ValidDateValues")] public void TryParseDate_AcceptsValidDates(string dateValue, DateTimeOffset expectedDate) { DateTimeOffset actualDate; Assert.True(FormattingUtilities.TryParseDate(dateValue, out actualDate)); Assert.Equal(expectedDate, actualDate); } [Theory] [PropertyData("InvalidDateValues")] public void TryStringToDate_RejectsInvalidDates(string dateValue) { DateTimeOffset actualDate; Assert.False(FormattingUtilities.TryParseDate(dateValue, out actualDate)); } [Theory] [InlineData("0", 0)] [InlineData("1", 1)] [InlineData("2147483647", Int32.MaxValue)] public void TryParseInt32_AcceptsValidNumbers(string intValue, int expectedInt) { int actualInt; Assert.True(FormattingUtilities.TryParseInt32(intValue, out actualInt)); Assert.Equal(expectedInt, actualInt); } [Theory] [InlineData("-2147483649")] [InlineData("-2147483648")] [InlineData("2147483648")] [InlineData(" 0")] public void TryParseInt32_RejectsInvalidNumbers(string intValue) { int actualInt; Assert.False(FormattingUtilities.TryParseInt32(intValue, out actualInt)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Handlers/HttpProgressEventArgsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http.Handlers { public class HttpProgressEventArgsTest { [Fact] public void Constructor_Initializes() { // Arrange int progressPercentage = 10; object userState = new object(); long bytesTransferred = 10L * 1024 * 1024 * 1024; long? totalBytes = 10L * 1024 * 1024 * 1024; // Act HttpProgressEventArgs args = new HttpProgressEventArgs(progressPercentage, userState, bytesTransferred, totalBytes); // Assert Assert.Equal(progressPercentage, args.ProgressPercentage); Assert.Equal(userState, args.UserState); Assert.Equal(bytesTransferred, args.BytesTransferred); Assert.Equal(totalBytes, args.TotalBytes); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Handlers/ProgressContentTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Handlers { public class ProgressContentTest { private const string TestHeader = "TestHeader"; private const string TestValue = "TestValue"; [Fact] public void Constructor_CopyHeadersFromInnerContent() { // Arrange StringContent innerContent = new StringContent("HelloWorld!"); innerContent.Headers.Add(TestHeader, TestValue); HttpRequestMessage request = new HttpRequestMessage(); ProgressMessageHandler progressHandler = new ProgressMessageHandler(); // Act ProgressContent progressContent = new ProgressContent(innerContent, progressHandler, request); // Assert ValidateContentHeader(progressContent); Assert.Equal(innerContent.Headers.ContentType, progressContent.Headers.ContentType); Assert.Equal(innerContent.Headers.ContentLength, progressContent.Headers.ContentLength); } [Fact] public async Task SerializeToStreamAsync_InsertsProgressStream() { // Arrange HttpRequestMessage request = new HttpRequestMessage(); request.Content = new StringContent("HelloWorld!"); MockProgressEventHandler progressEventHandler = new MockProgressEventHandler(); ProgressMessageHandler progressHandler = MockProgressEventHandler.CreateProgressMessageHandler(out progressEventHandler, sendProgress: true); ProgressContent progressContent = new ProgressContent(request.Content, progressHandler, request); MemoryStream memStream = new MemoryStream(); // Act await progressContent.CopyToAsync(memStream); // Assert Assert.True(progressEventHandler.WasInvoked); Assert.Equal(request, progressEventHandler.Sender); Assert.Equal(request.Content.Headers.ContentLength, progressEventHandler.EventArgs.TotalBytes); } [Fact] public void Dispose_DisposesInnerContent() { // Arrange StringContent innerContent = new StringContent("HelloWorld!"); HttpRequestMessage request = new HttpRequestMessage(); ProgressMessageHandler progressHandler = new ProgressMessageHandler(); ProgressContent progressContent = new ProgressContent(innerContent, progressHandler, request); // Act progressContent.Dispose(); // Assert Assert.ThrowsObjectDisposed(() => innerContent.LoadIntoBufferAsync(), typeof(StringContent).FullName); } private static void ValidateContentHeader(HttpContent content) { IEnumerable values; bool headerResult = content.Headers.TryGetValues(TestHeader, out values); Assert.True(headerResult); Assert.Equal(TestValue, values.First()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Handlers/ProgressMessageHandlerTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http.Handlers { public class ProgressMessageHandlerTest { private const string TestHeader = "TestHeader"; private const string TestValue = "TestValue"; [Theory] [InlineData(false, false)] [InlineData(false, true)] [InlineData(true, false)] [InlineData(true, true)] public async Task SendAsync_DoesNotInsertSendProgressWithoutEntityOrHandlerPresent(bool insertRequestEntity, bool addSendProgressHandler) { // Arrange HttpMessageInvoker invoker = CreateMessageInvoker(includeResponseEntity: false, addReceiveProgressHandler: false, addSendProgressHandler: addSendProgressHandler); HttpRequestMessage request = new HttpRequestMessage(); HttpContent content = null; if (insertRequestEntity) { content = new StringContent("Request Entity!"); content.Headers.Add(TestHeader, TestValue); request.Content = content; } // Act await invoker.SendAsync(request, CancellationToken.None); // Assert if (insertRequestEntity && addSendProgressHandler) { ValidateContentHeader(request.Content); Assert.NotSame(content, request.Content); Assert.IsType(request.Content); } else { if (insertRequestEntity) { Assert.IsType(request.Content); } else { Assert.Null(request.Content); } } } [Theory] [InlineData(false, false)] [InlineData(false, true)] [InlineData(true, false)] [InlineData(true, true)] public async Task SendAsync_InsertsReceiveProgressWhenResponseEntityPresent(bool insertResponseEntity, bool addReceiveProgressHandler) { // Arrange HttpMessageInvoker invoker = CreateMessageInvoker(includeResponseEntity: insertResponseEntity, addSendProgressHandler: false, addReceiveProgressHandler: addReceiveProgressHandler); HttpRequestMessage request = new HttpRequestMessage(); // Act HttpResponseMessage response = await invoker.SendAsync(request, CancellationToken.None); // Assert if (insertResponseEntity && addReceiveProgressHandler) { ValidateContentHeader(response.Content); Assert.NotNull(response.Content); Assert.IsType(response.Content); } else { if (insertResponseEntity) { Assert.IsType(response.Content); } else { Assert.NotNull(response.Content); Assert.Equal(0L, response.Content.Headers.ContentLength); } } } private static HttpMessageInvoker CreateMessageInvoker(bool includeResponseEntity, bool addSendProgressHandler, bool addReceiveProgressHandler) { ShortCircuitMessageHandler innerHandler = new ShortCircuitMessageHandler(includeResponseEntity); ProgressMessageHandler progress = new ProgressMessageHandler(innerHandler); if (addSendProgressHandler) { progress.HttpSendProgress += new MockProgressEventHandler().Handler; } if (addReceiveProgressHandler) { progress.HttpReceiveProgress += new MockProgressEventHandler().Handler; } return new HttpMessageInvoker(progress); } private static void ValidateContentHeader(HttpContent content) { IEnumerable values; bool headerResult = content.Headers.TryGetValues(TestHeader, out values); Assert.True(headerResult); Assert.Equal(TestValue, values.First()); } private class ShortCircuitMessageHandler : HttpMessageHandler { bool _includeResponseEntity; public ShortCircuitMessageHandler(bool includeResponseEntity) { _includeResponseEntity = includeResponseEntity; } protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpResponseMessage response = request.CreateResponse(); if (_includeResponseEntity) { response.Content = new StringContent("Response Entity"); response.Content.Headers.Add(TestHeader, TestValue); } return Task.FromResult(response); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Handlers/ProgressStreamTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Handlers { public class ProgressStreamTest { [Fact] public async Task Read_ReportsBytesRead() { // Arrange HttpResponseMessage response = CreateResponse(); Stream innerStream = await response.Content.ReadAsStreamAsync(); long? expectedLength = response.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: false); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, response: response); // Act/Assert int totalBytesRead = 0; int bytesRead = 0; do { byte[] buffer = new byte[8]; bytesRead = progressStream.Read(buffer, 0, buffer.Length); totalBytesRead += bytesRead; Assert.Equal(totalBytesRead, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesRead) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } while (bytesRead > 0); Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } [Fact] public async Task ReadByte_ReportsBytesRead() { // Arrange HttpResponseMessage response = CreateResponse(); Stream innerStream = await response.Content.ReadAsStreamAsync(); long? expectedLength = response.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: false); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, response: response); // Act/Assert int totalBytesRead = 0; while (progressStream.ReadByte() != -1) { totalBytesRead += 1; Assert.Equal(totalBytesRead, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesRead) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 [Fact] public async Task BeginEndRead_ReportsBytesRead() { // Arrange HttpResponseMessage response = CreateResponse(); Stream innerStream = await response.Content.ReadAsStreamAsync(); long? expectedLength = response.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: false); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, response: response); object userState = new object(); // Act/Assert int totalBytesRead = 0; int bytesRead = 0; do { byte[] buffer = new byte[8]; IAsyncResult result = progressStream.BeginRead(buffer, 0, buffer.Length, null, userState); bytesRead = progressStream.EndRead(result); totalBytesRead += bytesRead; Assert.Same(userState, mockProgressEventHandler.EventArgs.UserState); Assert.Equal(totalBytesRead, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesRead) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } while (bytesRead > 0); Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } #endif [Fact] public async Task ReadAsync_ReportsBytesRead() { // Arrange HttpResponseMessage response = CreateResponse(); Stream innerStream = await response.Content.ReadAsStreamAsync(); long? expectedLength = response.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: false); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, response: response); object userState = new object(); // Act/Assert int totalBytesRead = 0; int bytesRead = 0; do { byte[] buffer = new byte[8]; bytesRead = await progressStream.ReadAsync(buffer, 0, buffer.Length); totalBytesRead += bytesRead; Assert.Equal(totalBytesRead, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesRead) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } while (bytesRead > 0); Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } [Fact] public void Write_ReportsBytesWritten() { // Arrange HttpRequestMessage request = CreateRequest(); Stream innerStream = new MemoryStream(); byte[] buffer = CreateBufferContent(); long? expectedLength = request.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: true); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, request: request); // Act/Assert int totalBytesWritten = 0; int bytesWritten = 0; while (totalBytesWritten < expectedLength) { bytesWritten = Math.Min(8, (int)expectedLength - totalBytesWritten); progressStream.Write(buffer, totalBytesWritten, bytesWritten); totalBytesWritten += bytesWritten; Assert.Equal(totalBytesWritten, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesWritten) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } [Fact] public void WriteByte_ReportsBytesWritten() { // Arrange HttpRequestMessage request = CreateRequest(); Stream innerStream = new MemoryStream(); byte[] buffer = CreateBufferContent(); long? expectedLength = request.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: true); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, request: request); // Act/Assert int totalBytesWritten = 0; while (totalBytesWritten < expectedLength) { progressStream.WriteByte(buffer[totalBytesWritten]); totalBytesWritten += 1; Assert.Equal(totalBytesWritten, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesWritten) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 [Fact] public void BeginEndWrite_ReportsBytesWritten() { // Arrange using (ManualResetEvent writeComplete = new ManualResetEvent(false)) { HttpRequestMessage request = CreateRequest(); Stream innerStream = new MemoryStream(); byte[] buffer = CreateBufferContent(); long? expectedLength = request.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: true); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, request: request); object userState = new object(); // Act/Assert int totalBytesWritten = 0; int bytesWritten = 0; while (totalBytesWritten < expectedLength) { bytesWritten = Math.Min(8, (int)expectedLength - totalBytesWritten); IAsyncResult result = progressStream.BeginWrite(buffer, totalBytesWritten, bytesWritten, ia => { progressStream.EndWrite(ia); writeComplete.Set(); }, userState); writeComplete.WaitOne(); writeComplete.Reset(); totalBytesWritten += bytesWritten; Assert.Same(userState, mockProgressEventHandler.EventArgs.UserState); Assert.Equal(totalBytesWritten, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesWritten) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } } #endif [Fact] public async Task WriteAsync_ReportsBytesWritten() { // Arrange using (ManualResetEvent writeComplete = new ManualResetEvent(false)) { HttpRequestMessage request = CreateRequest(); Stream innerStream = new MemoryStream(); byte[] buffer = CreateBufferContent(); long? expectedLength = request.Content.Headers.ContentLength; MockProgressEventHandler mockProgressEventHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressEventHandler, sendProgress: true); ProgressStream progressStream = CreateProgressStream(innerStream: innerStream, progressMessageHandler: progressMessageHandler, request: request); object userState = new object(); // Act/Assert int totalBytesWritten = 0; int bytesWritten = 0; while (totalBytesWritten < expectedLength) { bytesWritten = Math.Min(8, (int)expectedLength - totalBytesWritten); await progressStream.WriteAsync(buffer, totalBytesWritten, bytesWritten); totalBytesWritten += bytesWritten; Assert.Equal(totalBytesWritten, mockProgressEventHandler.EventArgs.BytesTransferred); Assert.Equal((100L * totalBytesWritten) / expectedLength, mockProgressEventHandler.EventArgs.ProgressPercentage); } Assert.Equal(expectedLength, mockProgressEventHandler.EventArgs.TotalBytes); Assert.Equal(100, mockProgressEventHandler.EventArgs.ProgressPercentage); } } internal static ProgressStream CreateProgressStream( Stream innerStream = null, ProgressMessageHandler progressMessageHandler = null, HttpRequestMessage request = null, HttpResponseMessage response = null) { Stream iStream = innerStream ?? new Mock().Object; ProgressMessageHandler pHandler = progressMessageHandler ?? new ProgressMessageHandler(); HttpRequestMessage req = request ?? new HttpRequestMessage(); HttpResponseMessage rsp = response ?? new HttpResponseMessage() { Content = new StreamContent(Stream.Null) }; return new ProgressStream(iStream, pHandler, req, rsp); } private static HttpRequestMessage CreateRequest() { return new HttpRequestMessage { Content = CreateStringContent() }; } private static HttpResponseMessage CreateResponse() { return new HttpResponseMessage { Content = CreateStringContent() }; } private static String CreateContent() { StringBuilder content = new StringBuilder(); for (int count = 0; count < 100; count++) { content.Append("1234567890"); } return content.ToString(); } private static HttpContent CreateStringContent() { return new StringContent(CreateContent()); } private static byte[] CreateBufferContent() { return Encoding.UTF8.GetBytes(CreateContent()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Handlers/ProgressWriteAsyncResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Formatting.Mocks; using System.Text; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Handlers { public class ProgressWriteAsyncResultTest { static readonly byte[] sampleData = Encoding.UTF8.GetBytes("Hello World! Hello World! Hello World! Hello World! Hello World!"); [Fact] public void Constructor_BeginWriteOnInnerStream() { // Arrange Mock mockInnerStream = new Mock(); ProgressStream progressStream = ProgressStreamTest.CreateProgressStream(); // Act IAsyncResult result = new ProgressWriteAsyncResult( mockInnerStream.Object, progressStream, sampleData, 2, 4, null, null); // Assert mockInnerStream.Verify(s => s.BeginWrite(sampleData, 2, 4, It.IsAny(), It.IsAny()), Times.Once()); } [Fact] public void Constructor_CompletesSynchronouslyIfInnerStreamCompletesSynchronously() { // Arrange Mock mockInnerStream = new Mock(); object userState = new object(); IAsyncResult mockIAsyncResult = MockCompletedAsyncResult.Create(true, userState); mockInnerStream.Setup(s => s.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(mockIAsyncResult); ProgressStream progressStream = ProgressStreamTest.CreateProgressStream(); // Act IAsyncResult result = new ProgressWriteAsyncResult( mockInnerStream.Object, progressStream, sampleData, 2, 4, null, userState); // Assert Assert.True(result.IsCompleted); Assert.True(result.CompletedSynchronously); Assert.Same(userState, result.AsyncState); } [Fact] public void Constructor_CompletesWithExceptionIfInnerStreamThrows() { // Arrange Mock mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); ProgressStream progressStream = ProgressStreamTest.CreateProgressStream(); // Act IAsyncResult result = new ProgressWriteAsyncResult( mockInnerStream.Object, progressStream, sampleData, 2, 2, null, null); // Assert Assert.True(result.IsCompleted); Assert.Throws(() => ProgressWriteAsyncResult.End(result)); } [Theory] [InlineData(0, 10)] [InlineData(10, 1)] public void Constructor_ReportsBytesWritten(int offset, int count) { // Arrange Mock mockInnerStream = new Mock(); object userState = new object(); IAsyncResult mockIAsyncResult = MockCompletedAsyncResult.Create(true, userState); mockInnerStream.Setup(s => s.BeginWrite(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(mockIAsyncResult); MockProgressEventHandler mockProgressHandler; ProgressMessageHandler progressMessageHandler = MockProgressEventHandler.CreateProgressMessageHandler(out mockProgressHandler, sendProgress: true); HttpRequestMessage request = new HttpRequestMessage(); ProgressStream progressStream = ProgressStreamTest.CreateProgressStream(progressMessageHandler: progressMessageHandler, request: request); // Act IAsyncResult result = new ProgressWriteAsyncResult( mockInnerStream.Object, progressStream, sampleData, offset, count, null, userState); // Assert Assert.True(mockProgressHandler.WasInvoked); Assert.Same(request, mockProgressHandler.Sender); Assert.Equal(count, mockProgressHandler.EventArgs.BytesTransferred); Assert.Same(userState, mockProgressHandler.EventArgs.UserState); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Headers/CookieHeaderValueTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using Microsoft.TestCommon; namespace System.Net.Http.Headers { public class CookieHeaderValueTest { public static TheoryDataSet CookieHeaderDataSet { get { var dataset = new TheoryDataSet(); NameValueCollection nvc = new NameValueCollection(); nvc.Add("n1", "v1"); nvc.Add("n2", "v2"); nvc.Add("n3", "v3"); CookieHeaderValue header1 = new CookieHeaderValue("name1", nvc) { Domain = "domain1", Expires = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero), HttpOnly = true, MaxAge = TimeSpan.FromDays(1), Path = "path1", Secure = true }; dataset.Add(header1, "name1=n1=v1&n2=v2&n3=v3; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly"); CookieHeaderValue header2 = new CookieHeaderValue("name2", ""); dataset.Add(header2, "name2="); CookieHeaderValue header3 = new CookieHeaderValue("name2", "value2"); dataset.Add(header3, "name2=value2"); CookieHeaderValue header4 = new CookieHeaderValue("name4", "value4") { MaxAge = TimeSpan.FromDays(1), }; dataset.Add(header4, "name4=value4; max-age=86400"); CookieHeaderValue header5 = new CookieHeaderValue("name5", "value5") { Domain = "domain1", Expires = new DateTimeOffset(1994, 11, 6, 8, 49, 37, TimeSpan.Zero), }; dataset.Add(header5, "name5=value5; expires=Sun, 06 Nov 1994 08:49:37 GMT; domain=domain1"); return dataset; } } public static TheoryDataSet CookieHeaderDataSet_NoCookie { get { var dataSet = new TheoryDataSet(); foreach (var item in CookieHeaderDataSet) { dataSet.Add((string)item[1]); } return dataSet; } } public static TheoryDataSet InvalidCookieHeaderDataSet { get { return new TheoryDataSet { "expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1", "name=value; expires=Sun, 06 Nov 1994 08:49:37 ZZZ; max-age=86400; domain=domain1", "name=value; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=-86400; domain=domain1", }; } } [Fact] public void CookieHeaderValue_Ctor1_InitializesCorrectly() { CookieHeaderValue header = new CookieHeaderValue("cookie", "value"); CookieState cookie = Assert.Single(header.Cookies); Assert.Equal("cookie", cookie.Name); Assert.Equal("value", cookie.Values.AllKeys[0]); } [Fact] public void CookieHeaderValue_Ctor2_InitializesCorrectly() { NameValueCollection nvc = new NameValueCollection(); nvc.Add("name", "value"); CookieHeaderValue header = new CookieHeaderValue("cookie", nvc); CookieState cookie = Assert.Single(header.Cookies); Assert.Equal("cookie", cookie.Name); Assert.Equal("name", cookie.Values.AllKeys[0]); Assert.Equal("value", cookie.Values["name"]); } [Fact] public void CookieHeaderValue_Clone() { // Arrange NameValueCollection nvc = new NameValueCollection(); nvc.Add("name", "value"); CookieHeaderValue expectedValue = new CookieHeaderValue("cookie", nvc); expectedValue.Domain = "domain"; expectedValue.Expires = DateTimeOffset.Now; expectedValue.MaxAge = TimeSpan.FromDays(10); expectedValue.Path = "path"; expectedValue.HttpOnly = true; expectedValue.Secure = true; // Act CookieHeaderValue actualValue = expectedValue.Clone() as CookieHeaderValue; // Assert Assert.Equal(expectedValue.ToString(), actualValue.ToString()); } [Theory] [PropertyData("CookieHeaderDataSet")] public void CookieHeaderValue_ToString(CookieHeaderValue input, string expectedValue) { Assert.Equal(expectedValue, input.ToString()); } [Theory] [InlineData("")] [InlineData(null)] public void CookieHeaderValue_GetItem_ReturnsNullIfNameIsNullOrEmpty(string name) { CookieHeaderValue header = new CookieHeaderValue("cookie", "value"); Assert.Null(header[name]); } [Fact] public void CookieHeaderValue_GetItem_ReturnsCookie() { CookieHeaderValue header = new CookieHeaderValue("cookie", "value"); Assert.Single(header.Cookies); CookieState state = header["cookie"]; Assert.Equal("value", state.Value); } [Fact] public void CookieHeaderValue_GetItem_CreatesEmptyCookieIfNotAlreadyPresent() { CookieHeaderValue header = new CookieHeaderValue("cookie", "value"); CookieState state = header["newstate"]; Assert.Equal(2, header.Cookies.Count); Assert.Equal(String.Empty, state.Value); } [Theory] [PropertyData("CookieHeaderDataSet_NoCookie")] public void CookieHeaderValue_TryParse_AcceptsValidValues(string expectedValue) { CookieHeaderValue header; bool result = CookieHeaderValue.TryParse(expectedValue, out header); Assert.True(result); Assert.Equal(expectedValue, header.ToString()); } [Theory] [PropertyData("InvalidCookieHeaderDataSet")] public void CookieHeaderValue_TryParse_RejectsInvalidValues(string value) { CookieHeaderValue header; bool result = CookieHeaderValue.TryParse(value, out header); Assert.False(result); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Headers/CookieStateTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using Microsoft.TestCommon; namespace System.Net.Http.Headers { public class CookieStateTest { public static TheoryDataSet InvalidCookieNames { get { return new TheoryDataSet { "", "{acb}", "[acb]", "\"acb\"", "a,b", "a;b", "a\\b", }; } } public static TheoryDataSet EncodedCookieStateStrings { get { TheoryDataSet data = new TheoryDataSet { { "?", "%3F" }, { "=", "%3D" }, { "", "%3Cacb%3E" }, { "{acb}", "%7Bacb%7D" }, { "[acb]", "%5Bacb%5D" }, { "\"acb\"", "%22acb%22" }, { "a,b", "a%2Cb" }, { "a;b", "a%3Bb" }, { "a\\b", "a%5Cb" }, { "[]{}\\|!@#$%^&*()_-+=", "%5B%5D%7B%7D%5C%7C!%40%23%24%25%5E%26*()_-%2B%3D" }, }; return data; } } [Fact] public void CookieState_CtorThrowsOnNullName() { Assert.ThrowsArgumentNull(() => new CookieState(null, "value"), "name"); } [Theory] [PropertyData("InvalidCookieNames")] public void CookieState_CtorThrowsOnInvalidName(string name) { Assert.ThrowsArgument(() => new CookieState(name, "value"), "name"); } [Fact] public void CookieState_CtorThrowsOnNullNameValueCollection() { Assert.ThrowsArgumentNull(() => new CookieState("name", (NameValueCollection)null), "values"); } [Theory] [InlineData("name", "")] [InlineData("name", "value")] [InlineData("name", "+=\\[]{}!@#$%^&*()_")] public void CookieState_Ctor1InitializesCorrectly(string name, string value) { CookieState cookie = new CookieState(name, value); Assert.Equal(name, cookie.Name); Assert.Equal(value, cookie.Values.AllKeys[0]); Assert.Equal(value, cookie.Value); } [Fact] public void CookieState_Ctor2InitializesCorrectly() { // Arrange NameValueCollection nvc = new NameValueCollection(); nvc.Add("n1", "v1"); // Act CookieState cookie = new CookieState("name", nvc); // Assert Assert.Equal("name", cookie.Name); Assert.Single(cookie.Values); Assert.Equal("n1", cookie.Values.AllKeys[0]); Assert.Equal("v1", cookie.Values["n1"]); Assert.Equal("n1", cookie.Value); } [Fact] public void CookieState_Value() { CookieState cookie = new CookieState("name"); Assert.Equal(String.Empty, cookie.Value); cookie.Value = "value1"; Assert.Equal("value1", cookie.Value); cookie.Values.AllKeys[0] = "value2"; Assert.Equal("value2", cookie.Value); } [Fact] public void CookieState_ItemTreatsNullNameAsEmpty() { // Arrange CookieState state = new CookieState("name", "value"); // Act state[null] = "v1"; // Assert Assert.Equal("name=value&=v1", state.ToString()); } [Theory] [PropertyData("EncodedCookieStateStrings")] public void CookieState_ItemEncodesName(string subname, string encodedSubname) { // Arrange CookieState state = new CookieState("name", "value"); // Act state[subname] = "v1"; // Assert string value = String.Format("name=value&{0}=v1", encodedSubname); Assert.Equal(value, state.ToString()); } [Theory] [PropertyData("EncodedCookieStateStrings")] public void CookieState_ItemEncodesValue(string subvalue, string encodedSubvalue) { // Arrange CookieState state = new CookieState("name", "value"); // Act state["n1"] = subvalue; // Assert string value = String.Format("name=value&n1={0}", encodedSubvalue); Assert.Equal(value, state.ToString()); } [Fact] public void CookieState_Clone() { // Arrange NameValueCollection nvc = new NameValueCollection(); nvc.Add("n1", "v1"); CookieState expectedValue = new CookieState("name", nvc); // Act CookieState actualValue = expectedValue.Clone() as CookieState; // Assert Assert.Equal("name", actualValue.Name); Assert.Single(actualValue.Values); Assert.Equal("n1", actualValue.Values.AllKeys[0]); Assert.Equal("v1", actualValue.Values["n1"]); } [Theory] [PropertyData("EncodedCookieStateStrings")] public void CookieState_ToStringWithSingleValue(string subValue, string encodedSubvalue) { // Arrange CookieState cookie = new CookieState("name", subValue); // Act string actualValue = cookie.ToString(); // Assert string expectedValue = String.Format("name={0}", encodedSubvalue); Assert.Equal(expectedValue, actualValue); } [Theory] [PropertyData("EncodedCookieStateStrings")] public void CookieState_ToStringWithNameValueCollection(string subValue, string encodedSubvalue) { // Arrange NameValueCollection nvc = new NameValueCollection(); nvc.Add("n1", subValue); nvc.Add("n2", subValue); nvc.Add("n3", subValue); CookieState cookie = new CookieState("name", nvc); // Act string actualValue = cookie.ToString(); // Assert string expectedValue = String.Format("name=n1={0}&n2={0}&n3={0}", encodedSubvalue); Assert.Equal(expectedValue, actualValue); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpClientExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Net.Http.Mocks; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class HttpClientExtensionsTest { private const string InvalidUriMessage = #if NET6_0_OR_GREATER "An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set."; #else "An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set."; #endif private readonly MediaTypeFormatter _formatter = new MockMediaTypeFormatter { CallBase = true }; private readonly HttpClient _client; private readonly MediaTypeHeaderValue _mediaTypeHeader = MediaTypeHeaderValue.Parse("foo/bar; charset=utf-16"); public HttpClientExtensionsTest() { Mock handlerMock = new Mock { CallBase = true }; handlerMock .Setup(h => h.SendAsyncPublic(It.IsAny(), It.IsAny())) .Returns((HttpRequestMessage request, CancellationToken _) => Task.FromResult(new HttpResponseMessage() { RequestMessage = request })); _client = new HttpClient(handlerMock.Object); } [Fact] public void PostAsJsonAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsJsonAsync("http://www.example.com", new object()), "client"); } [Fact] public void PostAsJsonAsync_String_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsJsonAsync((string)null, new object()), InvalidUriMessage); } [Fact] public async Task PostAsJsonAsync_String_UsesJsonMediaTypeFormatter() { var response = await _client.PostAsJsonAsync("http://example.com", new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } [Fact] public void PostAsXmlAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsXmlAsync("http://www.example.com", new object()), "client"); } #if !Testing_NetStandard1_3 // Avoid "The configured formatter 'System.Net.Http.Formatting.XmlMediaTypeFormatter' cannot write an object of type 'Object'." [Fact] public void PostAsXmlAsync_String_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsXmlAsync((string)null, new object()), InvalidUriMessage); } [Fact] public async Task PostAsXmlAsync_String_UsesXmlMediaTypeFormatter() { var response = await _client.PostAsXmlAsync("http://example.com", new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } #endif [Fact] public void PostAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsync("http://www.example.com", new object(), new JsonMediaTypeFormatter(), "text/json"), "client"); } [Fact] public void PostAsync_String_WhenRequestUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsync((string)null, new object(), new JsonMediaTypeFormatter(), "text/json"), InvalidUriMessage); } [Fact] public async Task PostAsync_String_WhenRequestUriIsSet_CreatesRequestWithAppropriateUri() { _client.BaseAddress = new Uri("http://example.com/"); var response = await _client.PostAsync("myapi/", new object(), _formatter); var request = response.RequestMessage; Assert.Equal("http://example.com/myapi/", request.RequestUri.ToString()); } [Fact] public async Task PostAsync_String_WhenAuthoritativeMediaTypeIsSet_CreatesRequestWithAppropriateContentType() { var response = await _client.PostAsync("http://example.com/myapi/", new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); Assert.Equal("utf-16", request.Content.Headers.ContentType.CharSet); } [Fact] public async Task PostAsync_String_WhenAuthoritativeMediaTypeStringIsSet_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PostAsync("http://example.com/myapi/", new object(), _formatter, mediaType, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PostAsync_String_WhenAuthoritativeMediaTypeStringIsSetWithoutCT_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PostAsync("http://example.com/myapi/", new object(), _formatter, mediaType); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PostAsync_String_WhenFormatterIsSet_CreatesRequestWithObjectContentAndCorrectFormatter() { var response = await _client.PostAsync("http://example.com/myapi/", new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; var content = Assert.IsType>(request.Content); Assert.Same(_formatter, content.Formatter); } [Fact] public async Task PostAsync_String_IssuesPostRequest() { var response = await _client.PostAsync("http://example.com/myapi/", new object(), _formatter); var request = response.RequestMessage; Assert.Same(HttpMethod.Post, request.Method); } [Fact] public void PostAsync_String_WhenMediaTypeFormatterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _client.PostAsync("http://example.com", new object(), formatter: null), "formatter"); } [Fact] public void PutAsJsonAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsJsonAsync("http://www.example.com", new object()), "client"); } [Fact] public void PutAsJsonAsync_String_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsJsonAsync((string)null, new object()), InvalidUriMessage); } [Fact] public async Task PutAsJsonAsync_String_UsesJsonMediaTypeFormatter() { var response = await _client.PutAsJsonAsync("http://example.com", new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } [Fact] public void PutAsXmlAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsXmlAsync("http://www.example.com", new object()), "client"); } #if !Testing_NetStandard1_3 // Avoid "The configured formatter 'System.Net.Http.Formatting.XmlMediaTypeFormatter' cannot write an object of type 'Object'." [Fact] public void PutAsXmlAsync_String_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsXmlAsync((string)null, new object()), InvalidUriMessage); } [Fact] public async Task PutAsXmlAsync_String_UsesXmlMediaTypeFormatter() { var response = await _client.PutAsXmlAsync("http://example.com", new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } #endif [Fact] public void PutAsync_String_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsync("http://www.example.com", new object(), new JsonMediaTypeFormatter(), "text/json"), "client"); } [Fact] public void PutAsync_String_WhenRequestUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsync((string)null, new object(), new JsonMediaTypeFormatter(), "text/json"), InvalidUriMessage); } [Fact] public async Task PutAsync_String_WhenRequestUriIsSet_CreatesRequestWithAppropriateUri() { _client.BaseAddress = new Uri("http://example.com/"); var response = await _client.PutAsync("myapi/", new object(), _formatter); var request = response.RequestMessage; Assert.Equal("http://example.com/myapi/", request.RequestUri.ToString()); } [Fact] public async Task PutAsync_String_WhenAuthoritativeMediaTypeIsSet_CreatesRequestWithAppropriateContentType() { var response = await _client.PutAsync("http://example.com/myapi/", new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); Assert.Equal("utf-16", request.Content.Headers.ContentType.CharSet); } [Fact] public async Task PutAsync_String_WhenFormatterIsSet_CreatesRequestWithObjectContentAndCorrectFormatter() { var response = await _client.PutAsync("http://example.com/myapi/", new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; var content = Assert.IsType>(request.Content); Assert.Same(_formatter, content.Formatter); } [Fact] public async Task PutAsync_String_WhenAuthoritativeMediaTypeStringIsSet_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PutAsync("http://example.com/myapi/", new object(), _formatter, mediaType, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PutAsync_String_WhenAuthoritativeMediaTypeStringIsSetWithoutCT_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PutAsync("http://example.com/myapi/", new object(), _formatter, mediaType); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PutAsync_String_IssuesPutRequest() { var response = await _client.PutAsync("http://example.com/myapi/", new object(), _formatter); var request = response.RequestMessage; Assert.Same(HttpMethod.Put, request.Method); } [Fact] public void PutAsync_String_WhenMediaTypeFormatterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _client.PutAsync("http://example.com", new object(), formatter: null), "formatter"); } [Fact] public void PostAsJsonAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsJsonAsync(new Uri("http://www.example.com"), new object()), "client"); } [Fact] public void PostAsJsonAsync_Uri_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsJsonAsync((Uri)null, new object()), InvalidUriMessage); } [Fact] public async Task PostAsJsonAsync_Uri_UsesJsonMediaTypeFormatter() { var response = await _client.PostAsJsonAsync(new Uri("http://example.com"), new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } [Fact] public void PostAsXmlAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsXmlAsync(new Uri("http://www.example.com"), new object()), "client"); } #if !Testing_NetStandard1_3 // Avoid "The configured formatter 'System.Net.Http.Formatting.XmlMediaTypeFormatter' cannot write an object of type 'Object'." [Fact] public void PostAsXmlAsync_Uri_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsXmlAsync((Uri)null, new object()), InvalidUriMessage); } [Fact] public async Task PostAsXmlAsync_Uri_UsesXmlMediaTypeFormatter() { var response = await _client.PostAsXmlAsync(new Uri("http://example.com"), new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } #endif [Fact] public void PostAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PostAsync(new Uri("http://www.example.com"), new object(), new JsonMediaTypeFormatter(), "text/json"), "client"); } [Fact] public void PostAsync_Uri_WhenRequestUriIsNull_ThrowsException() { Assert.Throws(() => _client.PostAsync((Uri)null, new object(), new JsonMediaTypeFormatter(), "text/json"), InvalidUriMessage); } [Fact] public async Task PostAsync_Uri_WhenRequestUriIsSet_CreatesRequestWithAppropriateUri() { _client.BaseAddress = new Uri("http://example.com/"); var response = await _client.PostAsync(new Uri("myapi/", UriKind.Relative), new object(), _formatter); var request = response.RequestMessage; Assert.Equal("http://example.com/myapi/", request.RequestUri.ToString()); } [Fact] public async Task PostAsync_Uri_WhenAuthoritativeMediaTypeIsSet_CreatesRequestWithAppropriateContentType() { var response = await _client.PostAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); Assert.Equal("utf-16", request.Content.Headers.ContentType.CharSet); } [Fact] public async Task PostAsync_Uri_WhenFormatterIsSet_CreatesRequestWithObjectContentAndCorrectFormatter() { var response = await _client.PostAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; var content = Assert.IsType>(request.Content); Assert.Same(_formatter, content.Formatter); } [Fact] public async Task PostAsync_Uri_WhenAuthoritativeMediaTypeStringIsSet_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PostAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, mediaType, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PostAsync_Uri_WhenAuthoritativeMediaTypeStringIsSetWithoutCT_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PostAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, mediaType); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PostAsync_Uri_IssuesPostRequest() { var response = await _client.PostAsync(new Uri("http://example.com/myapi/"), new object(), _formatter); var request = response.RequestMessage; Assert.Same(HttpMethod.Post, request.Method); } [Fact] public void PostAsync_Uri_WhenMediaTypeFormatterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _client.PostAsync(new Uri("http://example.com"), new object(), formatter: null), "formatter"); } [Fact] public void PutAsJsonAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsJsonAsync(new Uri("http://www.example.com"), new object()), "client"); } [Fact] public void PutAsJsonAsync_Uri_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsJsonAsync((Uri)null, new object()), InvalidUriMessage); } [Fact] public async Task PutAsJsonAsync_Uri_UsesJsonMediaTypeFormatter() { var response = await _client.PutAsJsonAsync(new Uri("http://example.com"), new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } [Fact] public void PutAsXmlAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsXmlAsync(new Uri("http://www.example.com"), new object()), "client"); } #if !Testing_NetStandard1_3 // Avoid "The configured formatter 'System.Net.Http.Formatting.XmlMediaTypeFormatter' cannot write an object of type 'Object'." [Fact] public void PutAsXmlAsync_Uri_WhenUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsXmlAsync((Uri)null, new object()), InvalidUriMessage); } [Fact] public async Task PutAsXmlAsync_Uri_UsesXmlMediaTypeFormatter() { var response = await _client.PutAsXmlAsync(new Uri("http://example.com"), new object()); var content = Assert.IsType>(response.RequestMessage.Content); Assert.IsType(content.Formatter); } #endif [Fact] public void PutAsync_Uri_WhenClientIsNull_ThrowsException() { HttpClient client = null; Assert.ThrowsArgumentNull(() => client.PutAsync(new Uri("http://www.example.com"), new object(), new JsonMediaTypeFormatter(), "text/json"), "client"); } [Fact] public void PutAsync_Uri_WhenRequestUriIsNull_ThrowsException() { Assert.Throws(() => _client.PutAsync((Uri)null, new object(), new JsonMediaTypeFormatter(), "text/json"), InvalidUriMessage); } [Fact] public async Task PutAsync_Uri_WhenRequestUriIsSet_CreatesRequestWithAppropriateUri() { _client.BaseAddress = new Uri("http://example.com/"); var response = await _client.PutAsync(new Uri("myapi/", UriKind.Relative), new object(), _formatter); var request = response.RequestMessage; Assert.Equal("http://example.com/myapi/", request.RequestUri.ToString()); } [Fact] public async Task PutAsync_Uri_WhenAuthoritativeMediaTypeIsSet_CreatesRequestWithAppropriateContentType() { var response = await _client.PutAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); Assert.Equal("utf-16", request.Content.Headers.ContentType.CharSet); } [Fact] public async Task PutAsync_Uri_WhenFormatterIsSet_CreatesRequestWithObjectContentAndCorrectFormatter() { var response = await _client.PutAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, _mediaTypeHeader, CancellationToken.None); var request = response.RequestMessage; var content = Assert.IsType>(request.Content); Assert.Same(_formatter, content.Formatter); } [Fact] public async Task PutAsync_Uri_WhenAuthoritativeMediaTypeStringIsSet_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PutAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, mediaType, CancellationToken.None); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PutAsync_Uri_WhenAuthoritativeMediaTypeStringIsSetWithoutCT_CreatesRequestWithAppropriateContentType() { string mediaType = _mediaTypeHeader.MediaType; var response = await _client.PutAsync(new Uri("http://example.com/myapi/"), new object(), _formatter, mediaType); var request = response.RequestMessage; Assert.Equal("foo/bar", request.Content.Headers.ContentType.MediaType); } [Fact] public async Task PutAsync_Uri_IssuesPutRequest() { var response = await _client.PutAsync(new Uri("http://example.com/myapi/"), new object(), _formatter); var request = response.RequestMessage; Assert.Same(HttpMethod.Put, request.Method); } [Fact] public void PutAsync_Uri_WhenMediaTypeFormatterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _client.PutAsync(new Uri("http://example.com"), new object(), formatter: null), "formatter"); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpClientFactoryTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Mocks; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpClientFactoryTest { public static TheoryDataSet> InvalidPipelines { get { return new TheoryDataSet> { new List() { new MockDelegatingHandler(), new MockDelegatingHandler(new HttpClientHandler()) }, new List() { new MockDelegatingHandler(new HttpClientHandler()), new MockDelegatingHandler(), }, new List() { null, new MockDelegatingHandler(), }, new List() { new MockDelegatingHandler(), null, }, }; } } [Fact] public void Create1_SetsInnerHandler() { // Arrange MockDelegatingHandler handler = new MockDelegatingHandler(); // Act HttpClient client = HttpClientFactory.Create(handler); // Assert Assert.IsType(handler.InnerHandler); } [Fact] public void Create2_ThrowsOnNullInnerHandler() { Assert.ThrowsArgumentNull(() => HttpClientFactory.Create(null, new DelegatingHandler[0]), "innerHandler"); } [Fact] public void Create2_SetsInnerHandler() { // Arrange MockDelegatingHandler handler = new MockDelegatingHandler(); HttpClientHandler innerHandler = new HttpClientHandler(); // Act HttpClient client = HttpClientFactory.Create(innerHandler, handler); // Assert Assert.IsType(handler.InnerHandler); } [Theory] [PropertyData("InvalidPipelines")] public void CreatePipeline_ThrowsOnInvalidPipeline(IEnumerable handlers) { Assert.ThrowsArgument(() => HttpClientFactory.CreatePipeline(new HttpClientHandler(), handlers), "handlers"); } [Fact] public void CreatePipeline_ReturnsInnerHandler() { // Arrange HttpClientHandler innerHandler = new HttpClientHandler(); // Act HttpMessageHandler handler = HttpClientFactory.CreatePipeline(innerHandler, null); // Assert Assert.Same(innerHandler, handler); } [Theory] [InlineData(1)] [InlineData(4)] [InlineData(16)] public void CreatePipeline_WiresUpHandlers(int maxHandlerCount) { // Arrange List handlers = new List(); for (int handlerCount = 0; handlerCount < maxHandlerCount; handlerCount++) { handlers.Add(new MockDelegatingHandler()); } HttpClientHandler innerHandler = new HttpClientHandler(); // Act DelegatingHandler pipeline = HttpClientFactory.CreatePipeline(innerHandler, handlers) as DelegatingHandler; // Assert for (int index = 0; index < handlers.Count - 1; index++) { Assert.Same(handlers[index], pipeline); pipeline = pipeline.InnerHandler as DelegatingHandler; } Assert.Same(innerHandler, pipeline.InnerHandler); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpContentExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class HttpContentExtensionsTest { private static readonly IEnumerable _emptyFormatterList = Enumerable.Empty(); private readonly Mock _formatterMock = new Mock { CallBase = true }; private readonly MediaTypeHeaderValue _mediaType = new MediaTypeHeaderValue("foo/bar"); private readonly MediaTypeFormatter[] _formatters; public HttpContentExtensionsTest() { _formatterMock.Object.SupportedMediaTypes.Add(_mediaType); _formatters = new[] { _formatterMock.Object }; } [Fact] public void ReadAsAsync_WhenContentParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => HttpContentExtensions.ReadAsAsync(null, typeof(string), _emptyFormatterList), "content"); } [Fact] public void ReadAsAsync_WhenTypeParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => HttpContentExtensions.ReadAsAsync(new StringContent(""), null, _emptyFormatterList), "type"); } [Fact] public void ReadAsAsync_WhenFormattersParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => HttpContentExtensions.ReadAsAsync(new StringContent(""), typeof(string), null), "formatters"); } [Fact] public void ReadAsAsyncOfT_WhenContentParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => HttpContentExtensions.ReadAsAsync(null, _emptyFormatterList), "content"); } [Fact] public void ReadAsAsyncOfT_WhenFormattersParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => HttpContentExtensions.ReadAsAsync(new StringContent(""), null), "formatters"); } [Fact] public async Task ReadAsAsyncOfT_WhenContentIsObjectContent_GoesThroughSerializationCycleToConvertTypes() { var content = new ObjectContent(new int[] { 10, 20, 30, 40 }, new JsonMediaTypeFormatter()); byte[] result = await content.ReadAsAsync(); Assert.Equal(new byte[] { 10, 20, 30, 40 }, result); } [Fact] public void ReadAsAsyncOfT_WhenNoMatchingFormatterFound_Throws() { var content = new StringContent("{}"); content.Headers.ContentType = _mediaType; content.Headers.ContentType.CharSet = "utf-16"; var formatters = new MediaTypeFormatter[] { new JsonMediaTypeFormatter() }; Assert.Throws(() => content.ReadAsAsync>(formatters), "No MediaTypeFormatter is available to read an object of type 'List`1' from content with media type 'foo/bar'."); } [Fact] public void ReadAsAsyncOfT_WhenTypeIsReferenceTypeAndNoMediaType_Throws() { var content = new StringContent("{}"); content.Headers.ContentType = null; var formatters = new MediaTypeFormatter[] { new JsonMediaTypeFormatter() }; Assert.Throws(() => content.ReadAsAsync>(formatters), "No MediaTypeFormatter is available to read an object of type 'List`1' from content with media type 'application/octet-stream'."); } [Fact] public void ReadAsAsyncOfT_WhenTypeIsValueTypeAndNoMediaType_Throws() { var content = new StringContent("123456"); content.Headers.ContentType = null; var formatters = new MediaTypeFormatter[] { new JsonMediaTypeFormatter() }; Assert.Throws(() => content.ReadAsAsync(formatters), "No MediaTypeFormatter is available to read an object of type 'Int32' from content with media type 'application/octet-stream'."); } [Fact] public async Task ReadAsAsyncOfT_ReadsFromContent_ThenInvokesFormattersReadFromStreamMethod() { Stream contentStream = null; string value = "42"; var contentMock = new Mock { CallBase = true }; contentMock.Setup(c => c.SerializeToStreamAsyncPublic(It.IsAny(), It.IsAny())) .Returns(TaskHelpers.Completed) .Callback((Stream s, TransportContext _) => contentStream = s) .Verifiable(); HttpContent content = contentMock.Object; content.Headers.ContentType = _mediaType; _formatterMock .Setup(f => f.ReadFromStreamAsync(typeof(string), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.FromResult(value)); _formatterMock.Setup(f => f.CanReadType(typeof(string))).Returns(true); var resultValue = await content.ReadAsAsync(_formatters); Assert.Same(value, resultValue); contentMock.Verify(); _formatterMock.Verify(f => f.ReadFromStreamAsync(typeof(string), contentStream, content, null), Times.Once()); } [Fact] public async Task ReadAsAsyncOfT_InvokesFormatterEvenIfContentLengthIsZero() { var content = new StringContent(""); _formatterMock.Setup(f => f.CanReadType(typeof(string))).Returns(true); _formatterMock.Object.SupportedMediaTypes.Add(content.Headers.ContentType); _formatterMock .Setup(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(Task.FromResult(result: null)); await content.ReadAsAsync(_formatters); _formatterMock.Verify(f => f.ReadFromStreamAsync(typeof(string), It.IsAny(), content, It.IsAny()), Times.Once()); } [Fact] public async Task ReadAsAsync_WhenContentIsObjectContentAndValueIsCompatibleType_ReadsValueFromObjectContent() { _formatterMock.Setup(f => f.CanWriteType(typeof(TestClass))).Returns(true); var value = new TestClass(); var content = new ObjectContent(value, _formatterMock.Object); Assert.Same(value, await content.ReadAsAsync(_formatters)); Assert.Same(value, await content.ReadAsAsync(_formatters)); Assert.Same(value, await content.ReadAsAsync(typeof(object), _formatters)); Assert.Same(value, await content.ReadAsAsync(typeof(TestClass), _formatters)); _formatterMock.Verify(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), content, It.IsAny()), Times.Never()); } [Fact] public async Task ReadAsAsync_WhenContentIsObjectContentAndValueIsNull_IfTypeIsNullable_SerializesAndDeserializesValue() { _formatterMock.Setup(f => f.CanWriteType(typeof(object))).Returns(true); _formatterMock.Setup(f => f.CanReadType(It.IsAny())).Returns(true); var content = new ObjectContent(null, _formatterMock.Object); SetupUpRoundTripSerialization(type => null); Assert.Null(await content.ReadAsAsync(_formatters)); Assert.Null(await content.ReadAsAsync(_formatters)); Assert.Null(await content.ReadAsAsync>(_formatters)); Assert.Null(await content.ReadAsAsync(typeof(object), _formatters)); Assert.Null(await content.ReadAsAsync(typeof(TestClass), _formatters)); Assert.Null(await content.ReadAsAsync(typeof(Nullable), _formatters)); _formatterMock.Verify(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), content, It.IsAny()), Times.Exactly(6)); } [Fact] public async Task ReadAsAsync_WhenContentIsObjectContentAndValueIsNull_IfTypeIsNotNullable_SerializesAndDeserializesValue() { _formatterMock.Setup(f => f.CanWriteType(typeof(object))).Returns(true); _formatterMock.Setup(f => f.CanReadType(typeof(Int32))).Returns(true); var content = new ObjectContent(null, _formatterMock.Object, _mediaType); SetupUpRoundTripSerialization(); Assert.IsType(await content.ReadAsAsync(_formatters)); Assert.IsType(await content.ReadAsAsync(typeof(Int32), _formatters)); _formatterMock.Verify(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), content, It.IsAny()), Times.Exactly(2)); } [Fact] public async Task ReadAsAsync_WhenContentIsObjectContentAndValueIsNotCompatibleType_SerializesAndDeserializesValue() { _formatterMock.Setup(f => f.CanWriteType(typeof(TestClass))).Returns(true); _formatterMock.Setup(f => f.CanReadType(typeof(string))).Returns(true); var value = new TestClass(); var content = new ObjectContent(value, _formatterMock.Object, _mediaType); SetupUpRoundTripSerialization(type => new TestClass()); await Assert.ThrowsAsync(() => content.ReadAsAsync(_formatters)); Assert.IsNotType(await content.ReadAsAsync(typeof(string), _formatters)); _formatterMock.Verify(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), content, It.IsAny()), Times.Exactly(2)); } [Fact] public async Task ReadAsAsync_WhenContentIsMultipartContentAndFormatterCanReadFromTheContent() { MultipartContent mimeContent = new MultipartContent(); mimeContent.Add(new StringContent("multipartContent")); _formatterMock.Setup(f => f.CanWriteType(It.IsAny())).Returns(true); _formatterMock.Setup(f => f.CanReadType(It.IsAny())).Returns(true); _formatterMock.Setup(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(async (type, stream, content, logger) => { MultipartMemoryStreamProvider provider = await content.ReadAsMultipartAsync(); HttpContent providerContent = Assert.Single(provider.Contents); return await providerContent.ReadAsStringAsync(); }); MediaTypeFormatter formatter = _formatterMock.Object; formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/mixed")); Assert.Equal("multipartContent", await mimeContent.ReadAsAsync(new[] { formatter })); } [Fact] public async Task ReadAsAsync_type_cancellationToken_PassesCancellationTokenFurther() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = new StringContent("42", Encoding.Default, "application/json"); await Assert.ThrowsAsync(() => content.ReadAsAsync(typeof(int), cts.Token)); } [Fact] public async Task ReadAsAsync_type_formatters_cancellationToken_PassesCancellationTokenFurther() { // Arrange Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/test"); CancellationToken token = new CancellationToken(); Mock formatter = new Mock(MockBehavior.Strict); formatter.Object.SupportedMediaTypes.Add(content.Headers.ContentType); formatter.Setup(f => f.CanReadType(typeof(int))).Returns(true); formatter .Setup(f => f.ReadFromStreamAsync(typeof(int), It.IsAny(), content, null, token)) .Returns(Task.FromResult(42)) .Verifiable(); // Act await content.ReadAsAsync(typeof(int), new[] { formatter.Object }, token); // Assert formatter.Verify(); } [Fact] public async Task ReadAsAsync_type_formatters_formatterLogger_cancellationToken_PassesCancellationTokenFurther() { // Arrange Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/test"); CancellationToken token = new CancellationToken(); IFormatterLogger formatterLogger = new Mock().Object; Mock formatter = new Mock(MockBehavior.Strict); formatter.Object.SupportedMediaTypes.Add(content.Headers.ContentType); formatter.Setup(f => f.CanReadType(typeof(int))).Returns(true); formatter .Setup(f => f.ReadFromStreamAsync(typeof(int), It.IsAny(), content, formatterLogger, token)) .Returns(Task.FromResult(42)) .Verifiable(); // Act await content.ReadAsAsync(typeof(int), new[] { formatter.Object }, formatterLogger, token); // Assert formatter.Verify(); } [Fact] public Task ReadAsAsyncOfT_cancellationToken_PassesCancellationTokenFurther() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = new StringContent("42", Encoding.Default, "application/json"); return Assert.ThrowsAsync(() => content.ReadAsAsync(cts.Token)); } [Fact] public async Task ReadAsAsyncOfT_formatters_cancellationToken_PassesCancellationTokenFurther() { // Arrange Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/test"); CancellationToken token = new CancellationToken(); Mock formatter = new Mock(MockBehavior.Strict); formatter.Object.SupportedMediaTypes.Add(content.Headers.ContentType); formatter.Setup(f => f.CanReadType(typeof(int))).Returns(true); formatter .Setup(f => f.ReadFromStreamAsync(typeof(int), It.IsAny(), content, null, token)) .Returns(Task.FromResult(42)) .Verifiable(); // Act await content.ReadAsAsync(new[] { formatter.Object }, token); // Assert formatter.Verify(); } [Fact] public async Task ReadAsAsyncOfT_formatters_formatterLogger_cancellationToken_PassesCancellationTokenFurther() { // Arrange Stream stream = new MemoryStream(); HttpContent content = new StreamContent(stream); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/test"); CancellationToken token = new CancellationToken(); IFormatterLogger formatterLogger = new Mock().Object; Mock formatter = new Mock(MockBehavior.Strict); formatter.Object.SupportedMediaTypes.Add(content.Headers.ContentType); formatter.Setup(f => f.CanReadType(typeof(int))).Returns(true); formatter .Setup(f => f.ReadFromStreamAsync(typeof(int), It.IsAny(), content, formatterLogger, token)) .Returns(Task.FromResult(42)) .Verifiable(); // Act await content.ReadAsAsync(new[] { formatter.Object }, formatterLogger, token); // Assert formatter.Verify(); } private void SetupUpRoundTripSerialization(Func factory = null) { factory = factory ?? Activator.CreateInstance; _formatterMock.Setup(f => f.WriteToStreamAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(TaskHelpers.Completed()); _formatterMock.Setup(f => f.ReadFromStreamAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns((type, stream, content, logger) => Task.FromResult(factory(type))); } public class TestClass { } public abstract class TestableHttpContent : HttpContent { protected override Task CreateContentReadStreamAsync() { return CreateContentReadStreamAsyncPublic(); } public virtual Task CreateContentReadStreamAsyncPublic() { return base.CreateContentReadStreamAsync(); } protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { return SerializeToStreamAsyncPublic(stream, context); } public abstract Task SerializeToStreamAsyncPublic(Stream stream, TransportContext context); protected override bool TryComputeLength(out long length) { return TryComputeLengthPublic(out length); } public abstract bool TryComputeLengthPublic(out long length); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpContentFormDataExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpContentFormDataExtensionsTest { public static TheoryDataSet FormDataContentTypes { get { return new TheoryDataSet { "application/x-www-form-urlencoded", "APPLICATION/x-www-form-urlencoded", "application/X-WWW-FORM-URLENCODED", "application/x-www-form-urlencoded; charset=utf-8", "application/x-www-form-urlencoded; parameter=value", }; } } public static TheoryDataSet NonFormDataContentTypes { get { return new TheoryDataSet { "application/xml", "APPLICATION/json", "text/xml", "text/xml; charset=utf-8", "text/xml; parameter=value", }; } } public static TheoryDataSet FormData { get { return new TheoryDataSet { "a=b", "a+c=d+e", "n1=v1&n2=v2", "n1=v1a+v1b&n2=v2a+v2b", }; } } public static TheoryDataSet IrregularFormData { get { return new TheoryDataSet { "?data", Char.ConvertFromUtf32(0x0D), "Hello World", "Hello World", "{ \"Message\" : \"Hello World\"", }; } } [Fact] public void IsFormData_ThrowsOnNull() { Assert.ThrowsArgumentNull(() => HttpContentFormDataExtensions.IsFormData(null), "content"); } [Fact] public void IsFormData_HandlesNullContentType() { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = null; Assert.False(content.IsFormData()); } [Theory] [PropertyData("FormDataContentTypes")] public void IsFormData_AcceptsFormDataMediaTypes(string mediaType) { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); Assert.True(content.IsFormData()); } [Theory] [PropertyData("NonFormDataContentTypes")] public void IsFormData_RejectsNonFormDataMediaTypes(string mediaType) { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); Assert.False(content.IsFormData()); } [Fact] public void ReadAsFormDataAsync_ThrowsOnNull() { Assert.ThrowsArgumentNull(() => HttpContentFormDataExtensions.ReadAsFormDataAsync(null), "content"); } [Fact] public async Task ReadAsFromDataAsync_PassesCancellationToken() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = new StringContent(""); content.Headers.ContentType = MediaTypeConstants.ApplicationFormUrlEncodedMediaType; await Assert.ThrowsAsync(() => content.ReadAsFormDataAsync(cts.Token)); } [Theory] [PropertyData("FormData")] public async Task ReadAsFormDataAsync_HandlesFormData(string formData) { // Arrange HttpContent content = new StringContent(formData); content.Headers.ContentType = MediaTypeConstants.ApplicationFormUrlEncodedMediaType; // Act NameValueCollection data = await content.ReadAsFormDataAsync(); // Assert Assert.Equal(formData, data.ToString()); } [Fact] public async Task ReadAsFormDataAsync_HandlesFormData_Encoded() { // Arrange string formData = "N=%c3%a6%c3%b8%c3%a5"; HttpContent content = new StringContent(formData); content.Headers.ContentType = MediaTypeConstants.ApplicationFormUrlEncodedMediaType; // Act NameValueCollection data = await content.ReadAsFormDataAsync(); // Assert Assert.Equal(formData, data.ToString(), ignoreCase: true); } [Theory] [PropertyData("IrregularFormData")] public async Task ReadAsFormDataAsync_HandlesIrregularFormData(string irregularFormData) { // Arrange HttpContent content = new StringContent(irregularFormData); content.Headers.ContentType = MediaTypeConstants.ApplicationFormUrlEncodedMediaType; // Act NameValueCollection data = await content.ReadAsFormDataAsync(); // Assert Assert.Single(data); Assert.NotNull(data[irregularFormData]); } [Fact] public Task ReadAsFormDataAsync_HandlesNonFormData() { HttpContent content = new StringContent("{}", Encoding.UTF8, "test/unknown"); return Assert.ThrowsAsync(() => content.ReadAsFormDataAsync()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpContentMessageExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpContentMessageExtensionsTests { public static TheoryDataSet> ClientRoundTripData { get { return new TheoryDataSet> { new string[] { @"GET / HTTP/1.1", @"Accept: text/html, application/xhtml+xml, */*", @"Accept-Language: en-US", @"User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)", @"Accept-Encoding: gzip, deflate", @"Proxy-Connection: Keep-Alive", @"Host: msdn.microsoft.com", @"Cookie: omniID=1297715979621_9f45_1519_3f8a_f22f85346ac6; WT_FPC=id=65.55.227.138-2323234032.30136233:lv=1309374389020:ss=1309374389020; A=I&I=AxUFAAAAAACNCAAADYEZ7CFPss7Swnujy4PXZA!!&M=1&CS=126mAa0002ZB51a02gZB51a; MC1=GUID=568428660ad44d4ab8f46133f4b03738&HASH=6628&LV=20113&V=3; WT_NVR_RU=0=msdn:1=:2=; MUID=A44DE185EA1B4E8088CCF7B348C5D65F; MSID=Microsoft.CreationDate=03/04/2011 23:38:15&Microsoft.LastVisitDate=06/20/2011 04:15:08&Microsoft.VisitStartDate=06/20/2011 04:15:08&Microsoft.CookieId=f658f3f2-e6d6-42ab-b86b-96791b942b6f&Microsoft.TokenId=ffffffff-ffff-ffff-ffff-ffffffffffff&Microsoft.NumberOfVisits=106&Microsoft.CookieFirstVisit=1&Microsoft.IdentityToken=AA==&Microsoft.MicrosoftId=0441-6141-1523-9969; msresearch=%7B%22version%22%3A%224.6%22%2C%22state%22%3A%7B%22name%22%3A%22IDLE%22%2C%22url%22%3Aundefined%2C%22timestamp%22%3A1299281911415%7D%2C%22lastinvited%22%3A1299281911415%2C%22userid%22%3A%2212992819114151265672533023080%22%2C%22vendorid%22%3A1%2C%22surveys%22%3A%5Bundefined%5D%7D; CodeSnippetContainerLang=C#; msdn=L=1033; ADS=SN=175A21EF; s_cc=true; s_sq=%5B%5BB%5D%5D; TocHashCookie=ms310241(n)/aa187916(n)/aa187917(n)/dd273952(n)/dd295083(n)/ff472634(n)/ee667046(n)/ee667070(n)/gg259047(n)/gg618436(n)/; WT_NVR=0=/:1=query|library|en-us:2=en-us/vcsharp|en-us/library", @"Cookie: omniID=1297715979621_9f45_1519_3f8a_f22f85346ac6; WT_FPC=id=65.55.227.138-2323234032.30136233:lv=1309374389020:ss=1309374389020; A=I&I=AxUFAAAAAACNCAAADYEZ7CFPss7Swnujy4PXZA!!&M=1&CS=126mAa0002ZB51a02gZB51a; MC1=GUID=568428660ad44d4ab8f46133f4b03738&HASH=6628&LV=20113&V=3; WT_NVR_RU=0=msdn:1=:2=; MUID=A44DE185EA1B4E8088CCF7B348C5D65F; MSID=Microsoft.CreationDate=03/04/2011 23:38:15&Microsoft.LastVisitDate=06/20/2011 04:15:08&Microsoft.VisitStartDate=06/20/2011 04:15:08&Microsoft.CookieId=f658f3f2-e6d6-42ab-b86b-96791b942b6f&Microsoft.TokenId=ffffffff-ffff-ffff-ffff-ffffffffffff&Microsoft.NumberOfVisits=106&Microsoft.CookieFirstVisit=1&Microsoft.IdentityToken=AA==&Microsoft.MicrosoftId=0441-6141-1523-9969; msresearch=%7B%22version%22%3A%224.6%22%2C%22state%22%3A%7B%22name%22%3A%22IDLE%22%2C%22url%22%3Aundefined%2C%22timestamp%22%3A1299281911415%7D%2C%22lastinvited%22%3A1299281911415%2C%22userid%22%3A%2212992819114151265672533023080%22%2C%22vendorid%22%3A1%2C%22surveys%22%3A%5Bundefined%5D%7D; CodeSnippetContainerLang=C#; msdn=L=1033; ADS=SN=175A21EF; s_cc=true; s_sq=%5B%5BB%5D%5D; TocHashCookie=ms310241(n)/aa187916(n)/aa187917(n)/dd273952(n)/dd295083(n)/ff472634(n)/ee667046(n)/ee667070(n)/gg259047(n)/gg618436(n)/; WT_NVR=0=/:1=query|library|en-us:2=en-us/vcsharp|en-us/library", }, new string[] { @"GET / HTTP/1.1", @"Host: msdn.microsoft.com", @"Proxy-Connection: keep-alive", @"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0", @"Accept: text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8", @"Accept-Language: en-us, en; q=0.5", @"Accept-Encoding: gzip, deflate", @"Accept-Charset: ISO-8859-1, utf-8; q=0.7, *; q=0.7", }, new string[] { @"GET / HTTP/1.1", @"Host: msdn.microsoft.com", @"Proxy-Connection: keep-alive", @"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30", @"Accept: text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8", @"Accept-Encoding: gzip, deflate, sdch", @"Accept-Language: en-US, en; q=0.8", @"Accept-Charset: ISO-8859-1, utf-8; q=0.7, *; q=0.3", }, new string[] { @"GET / HTTP/1.1", @"Host: msdn.microsoft.com", @"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1", @"Accept: application/xml, application/xhtml+xml, text/html; q=0.9, text/plain; q=0.8, image/png, */*; q=0.5", @"Accept-Language: en-US", @"Accept-Encoding: gzip, deflate", @"Connection: keep-alive", @"Proxy-Connection: keep-alive", }, new string[] { @"GET / HTTP/1.0", @"User-Agent: Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", @"Host: msdn.microsoft.com", @"Accept: text/html, application/xml; q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*; q=0.1", @"Accept-Language: en-US, en; q=0.9", @"Accept-Encoding: gzip, deflate", @"Proxy-Connection: Keep-Alive", }, }; } } public static TheoryDataSet> ServerRoundTripData { get { return new TheoryDataSet> { new string[] { @"HTTP/1.1 200 OK", @"Server: nginx", @"Date: Mon, 26 Dec 2011 16:33:07 GMT", @"Connection: keep-alive", @"Set-Cookie: CG=US:WA:Bellevue; path=/", @"Vary: Accept-Encoding, User-Agent", @"Cache-Control: max-age=60, private", @"Content-Length: 124", @"Content-Type: text/html; charset=UTF-8", }, new string[] { @"HTTP/1.1 302 Found", @"Proxy-Connection: Keep-Alive", @"Connection: Keep-Alive", @"Via: 1.1 RED-PRXY-23", @"Date: Thu, 30 Jun 2011 00:16:35 GMT", @"Location: /en-us/", @"Server: Microsoft-IIS/7.5", @"Cache-Control: private", @"P3P: CP=""ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI"", CP=""ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI""", @"Set-Cookie: A=I&I=AxUFAAAAAAD7BwAA8Jx0njhGoW3MGASDmzeaGw!!&M=1; domain=.microsoft.com; expires=Sun, 30-Jun-2041 00:16:35 GMT; path=/", @"Set-Cookie: ADS=SN=175A21EF; domain=.microsoft.com; path=/", @"Set-Cookie: Sto.UserLocale=en-us; path=/", @"Set-Cookie: A=I&I=AxUFAAAAAAD7BwAA8Jx0njhGoW3MGASDmzeaGw!!&M=1; domain=.microsoft.com; expires=Sun, 30-Jun-2041 00:16:35 GMT; path=/; path=/", @"Set-Cookie: ADS=SN=175A21EF; domain=.microsoft.com; path=/; path=/", @"X-AspNetMvc-Version: 3.0", @"X-AspNet-Version: 4.0.30319", @"X-Powered-By: ASP.NET", @"X-Powered-By: ASP.NET", @"Content-Length: 124", @"Content-Type: text/html; charset=utf-8", }, new string[] { @"HTTP/1.1 200 OK", @"Proxy-Connection: Keep-Alive", @"Connection: Keep-Alive", @"Transfer-Encoding: chunked", @"Via: 1.1 RED-PRXY-07", @"Date: Mon, 26 Dec 2011 19:11:47 GMT", @"Server: gws", @"Cache-Control: max-age=0, private", @"Set-Cookie: PREF=ID=e91cfd77b562e989:FF=0:TM=1324926707:LM=1324926707:S=4w8_eSySJPXCCjhT; expires=Wed, 25-Dec-2013 19:11:47 GMT; path=/; domain=.google.com", @"Set-Cookie: NID=54=bSMpxl0q0MVlvG-eZYSBtQuYTF1clqrA-TSIZT8wZcbhrrsdkP9G5zPiXGSBmiNu656QR3xfTXKUPkP-HqY_nSnsjj1fb-ipoZ3DUcyXb9oS9_8tjz3NZ3A44GPCmRPx; expires=Tue, 26-Jun-2012 19:11:47 GMT; path=/; domain=.google.com; HttpOnly", @"P3P: CP=""This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.""", @"X-XSS-Protection: 1; mode=block", @"X-Frame-Options: SAMEORIGIN", @"Expires: -1", @"Content-Type: text/html; charset=ISO-8859-1", } }; } } public static TheoryDataSet NotHttpMessageContent { get { return new TheoryDataSet { new ByteArrayContent(new byte[] { }), new StringContent(String.Empty), new StringContent(String.Empty, Encoding.UTF8, "application/http"), }; } } private static HttpContent CreateContent(bool isRequest, bool hasEntity) { string message; if (isRequest) { message = hasEntity ? ParserData.HttpRequestWithEntity : ParserData.HttpRequest; } else { message = hasEntity ? ParserData.HttpResponseWithEntity : ParserData.HttpResponse; } StringContent content = new StringContent(message); content.Headers.ContentType = isRequest ? ParserData.HttpRequestMediaType : ParserData.HttpResponseMediaType; return content; } private static HttpContent CreateContent(bool isRequest, IEnumerable header, string body) { StringBuilder message = new StringBuilder(); foreach (string h in header) { message.Append(h); message.Append("\r\n"); } message.Append("\r\n"); if (body != null) { message.Append(body); } StringContent content = new StringContent(message.ToString()); content.Headers.ContentType = isRequest ? ParserData.HttpRequestMediaType : ParserData.HttpResponseMediaType; return content; } private static async Task ValidateEntityAsync(HttpContent content) { Assert.NotNull(content); Assert.Equal(ParserData.TextContentType, content.Headers.ContentType.ToString()); string entity = await content.ReadAsStringAsync(); Assert.Equal(ParserData.HttpMessageEntity, entity); } private static async Task ValidateRequestMessageAsync(HttpRequestMessage request, bool hasEntity) { Assert.NotNull(request); Assert.Equal(Version.Parse("1.2"), request.Version); Assert.Equal(ParserData.HttpMethod, request.Method.ToString()); Assert.Equal(ParserData.HttpRequestUri, request.RequestUri); Assert.Equal(ParserData.HttpHostName, request.Headers.Host); Assert.True(request.Headers.Contains("N1"), "request did not contain expected N1 header."); Assert.True(request.Headers.Contains("N2"), "request did not contain expected N2 header."); if (hasEntity) { await ValidateEntityAsync(request.Content); } } private static async Task ValidateResponseMessageAsync(HttpResponseMessage response, bool hasEntity) { Assert.NotNull(response); Assert.Equal(new Version("1.2"), response.Version); Assert.Equal(ParserData.HttpStatus, response.StatusCode); Assert.Equal(ParserData.HttpReasonPhrase, response.ReasonPhrase); Assert.True(response.Headers.Contains("N1"), "Response did not contain expected N1 header."); Assert.True(response.Headers.Contains("N2"), "Response did not contain expected N2 header."); if (hasEntity) { await ValidateEntityAsync(response.Content); } } [Fact] public void ReadAsHttpRequestMessageAsync_VerifyArguments() { Assert.ThrowsArgumentNull(() => HttpContentMessageExtensions.ReadAsHttpRequestMessageAsync(null), "content"); Assert.ThrowsArgument(() => new ByteArrayContent(new byte[] { }).ReadAsHttpRequestMessageAsync(), "content"); Assert.ThrowsArgument(() => new StringContent(String.Empty).ReadAsHttpRequestMessageAsync(), "content"); Assert.ThrowsArgument(() => new StringContent(String.Empty, Encoding.UTF8, "application/http").ReadAsHttpRequestMessageAsync(), "content"); Assert.ThrowsArgument(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpResponseMediaType; content.ReadAsHttpRequestMessageAsync(); }, "content"); Assert.ThrowsArgumentNull(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpRequestMediaType; content.ReadAsHttpRequestMessageAsync(null); }, "uriScheme"); Assert.ThrowsArgument(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpRequestMediaType; content.ReadAsHttpRequestMessageAsync("i n v a l i d"); }, "uriScheme"); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpRequestMediaType; content.ReadAsHttpRequestMessageAsync(Uri.UriSchemeHttp, ParserData.MinBufferSize - 1); }, "bufferSize", ParserData.MinBufferSize.ToString(), ParserData.MinBufferSize - 1); } [Fact] public void ReadAsHttpResponseMessageAsync_VerifyArguments() { Assert.ThrowsArgumentNull(() => HttpContentMessageExtensions.ReadAsHttpResponseMessageAsync(null), "content"); Assert.ThrowsArgument(() => new ByteArrayContent(new byte[] { }).ReadAsHttpResponseMessageAsync(), "content"); Assert.ThrowsArgument(() => new StringContent(String.Empty).ReadAsHttpResponseMessageAsync(), "content"); Assert.ThrowsArgument(() => new StringContent(String.Empty, Encoding.UTF8, "application/http").ReadAsHttpResponseMessageAsync(), "content"); Assert.ThrowsArgument(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpRequestMediaType; content.ReadAsHttpResponseMessageAsync(); }, "content"); Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpResponseMediaType; content.ReadAsHttpResponseMessageAsync(ParserData.MinBufferSize - 1); }, "bufferSize", ParserData.MinBufferSize.ToString(), ParserData.MinBufferSize - 1); } [Fact] public void IsHttpRequestMessageContentVerifyArguments() { Assert.ThrowsArgumentNull(() => HttpContentMessageExtensions.IsHttpRequestMessageContent(null), "content"); } [Fact] public void IsHttpResponseMessageContentVerifyArguments() { Assert.ThrowsArgumentNull(() => { HttpContent content = null; HttpContentMessageExtensions.IsHttpResponseMessageContent(content); }, "content"); } [Theory] [PropertyData("NotHttpMessageContent")] public void IsHttpRequestMessageContentRespondsFalse(HttpContent notHttpMessageContent) { Assert.False(notHttpMessageContent.IsHttpRequestMessageContent()); } [Fact] public void IsHttpRequestMessageContentRespondsTrue() { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpRequestMediaType; Assert.True(content.IsHttpRequestMessageContent(), "Content should be HTTP request."); } [Theory] [PropertyData("NotHttpMessageContent")] public void IsHttpResponseMessageContent(HttpContent notHttpMessageContent) { Assert.False(notHttpMessageContent.IsHttpResponseMessageContent()); } [Fact] public void IsHttpResponseMessageContentRespondsTrue() { HttpContent content = new StringContent(String.Empty); content.Headers.ContentType = ParserData.HttpResponseMediaType; Assert.True(content.IsHttpResponseMessageContent(), "Content should be HTTP response."); } [Fact] public async Task ReadAsHttpRequestMessageAsync_RequestWithoutEntity_ShouldReturnHttpRequestMessage() { HttpContent content = CreateContent(isRequest: true, hasEntity: false); HttpRequestMessage httpRequest = await content.ReadAsHttpRequestMessageAsync(); await ValidateRequestMessageAsync(httpRequest, hasEntity: false); } [Fact] public async Task ReadAsHttpRequestMessageAsync_RequestWithEntity_ShouldReturnHttpRequestMessage() { HttpContent content = CreateContent(isRequest: true, hasEntity: true); HttpRequestMessage httpRequest = await content.ReadAsHttpRequestMessageAsync(); await ValidateRequestMessageAsync(httpRequest, hasEntity: true); } [Fact] public async Task ReadAsHttpRequestMessageAsync_WithHttpsUriScheme_ReturnsUriWithHttps() { HttpContent content = CreateContent(isRequest: true, hasEntity: true); HttpRequestMessage httpRequest = await content.ReadAsHttpRequestMessageAsync(Uri.UriSchemeHttps); Assert.Equal(ParserData.HttpsRequestUri, httpRequest.RequestUri); } [Fact] public async Task ReadAsHttpResponseMessageAsync_ResponseWithoutEntity_ShouldReturnHttpResponseMessage() { HttpContent content = CreateContent(isRequest: false, hasEntity: false); HttpResponseMessage httpResponse = await content.ReadAsHttpResponseMessageAsync(); await ValidateResponseMessageAsync(httpResponse, hasEntity: false); } [Fact] public async Task ReadAsHttpResponseMessageAsync_ResponseWithEntity_ShouldReturnHttpResponseMessage() { HttpContent content = CreateContent(isRequest: false, hasEntity: true); HttpResponseMessage httpResponse = await content.ReadAsHttpResponseMessageAsync(); await ValidateResponseMessageAsync(httpResponse, hasEntity: true); } [Fact] public Task ReadAsHttpRequestMessageAsync_NoHostHeader_Throws() { string[] request = new[] { @"GET / HTTP/1.1", }; HttpContent content = CreateContent(true, request, null); return Assert.ThrowsAsync(() => content.ReadAsHttpRequestMessageAsync()); } [Fact] public Task ReadAsHttpRequestMessageAsync_TwoHostHeaders_Throws() { string[] request = new[] { @"GET / HTTP/1.1", @"Host: somehost.com", @"Host: otherhost.com", }; HttpContent content = CreateContent(true, request, null); return Assert.ThrowsAsync(() => content.ReadAsHttpRequestMessageAsync()); } [Fact] public async Task ReadAsHttpRequestMessageAsync_SortHeaders() { string[] request = new[] { @"GET / HTTP/1.1", @"Host: somehost.com", @"Content-Language: xx", @"Request-Header: zz", }; HttpContent content = CreateContent(true, request, "sample body"); HttpRequestMessage httpRequest = await content.ReadAsHttpRequestMessageAsync(); Assert.Equal("xx", httpRequest.Content.Headers.ContentLanguage.ToString()); IEnumerable requestHeaderValues; Assert.True(httpRequest.Headers.TryGetValues("request-header", out requestHeaderValues)); Assert.Equal("zz", requestHeaderValues.First()); } [Fact] public async Task ReadAsHttpResponseMessageAsync_SortHeaders() { string[] response = new[] { @"HTTP/1.1 200 OK", @"Content-Language: xx", @"Response-Header: zz", }; HttpContent content = CreateContent(false, response, "sample body"); HttpResponseMessage httpResponse = await content.ReadAsHttpResponseMessageAsync(); Assert.Equal("xx", httpResponse.Content.Headers.ContentLanguage.ToString()); IEnumerable ResponseHeaderValues; Assert.True(httpResponse.Headers.TryGetValues("Response-header", out ResponseHeaderValues)); Assert.Equal("zz", ResponseHeaderValues.First()); } [Fact] public Task ReadAsHttpResponseMessageAsync_Throws_TheHeaderSizeExceededTheDefaultLimit() { string[] response = new[] { @"HTTP/1.1 200 OK", String.Format("Set-Cookie: {0}={1}", new String('a', 16 * 1024), new String('b', 16 * 1024)) }; return Assert.ThrowsAsync(() => { HttpContent content = CreateContent(false, response, "sample body"); return content.ReadAsHttpResponseMessageAsync(); }); } [Fact] public Task ReadAsHttpRequestMessageAsync_Throws_TheHeaderSizeExceededTheDefaultLimit() { string[] request = new[] { @"GET / HTTP/1.1", @"Host: msdn.microsoft.com", String.Format("Cookie: {0}={1}", new String('a', 16 * 1024), new String('b', 16 * 1024)) }; return Assert.ThrowsAsync(() => { HttpContent content = CreateContent(true, request, "sample body"); return content.ReadAsHttpRequestMessageAsync(); }); } [Fact] public Task ReadAsHttpResponseMessageAsync_LargeHeaderSize() { string[] response = new[] { @"HTTP/1.1 200 OK", String.Format("Set-Cookie: {0}={1}", new String('a', 16 * 1024), new String('b', 16 * 1024)) }; HttpContent content = CreateContent(false, response, "sample body"); return content.ReadAsHttpResponseMessageAsync(64 * 1024, 64 * 1024); } [Fact] public async Task ReadAsHttpRequestMessageAsync_LargeHeaderSize() { string cookieValue = string.Format("{0}={1}", new String('a', 16 * 1024), new String('b', 16 * 1024)); string[] request = new[] { @"GET / HTTP/1.1", @"Host: msdn.microsoft.com", string.Format("Cookie: {0}", cookieValue), }; HttpContent content = CreateContent(true, request, "sample body"); var httpRequestMessage = await content.ReadAsHttpRequestMessageAsync(Uri.UriSchemeHttp, 64 * 1024, 64 * 1024); Assert.Equal(HttpMethod.Get, httpRequestMessage.Method); Assert.Equal("/", httpRequestMessage.RequestUri.PathAndQuery); Assert.Equal("msdn.microsoft.com", httpRequestMessage.Headers.Host); IEnumerable actualCookieValue; Assert.True(httpRequestMessage.Headers.TryGetValues("Cookie", out actualCookieValue)); Assert.Equal(cookieValue, Assert.Single(actualCookieValue)); } [Fact] public async Task ReadAsHttpRequestMessageAsync_LargeHttpRequestLine() { string requestPath = string.Format("/myurl?{0}={1}", new string('a', 4 * 1024), new string('b', 4 * 1024)); string cookieValue = string.Format("{0}={1}", new String('a', 4 * 1024), new String('b', 4 * 1024)); string[] request = new[] { string.Format("GET {0} HTTP/1.1", requestPath), @"Host: msdn.microsoft.com", string.Format("Cookie: {0}", cookieValue), }; HttpContent content = CreateContent(true, request, "sample body"); var httpRequestMessage = await content.ReadAsHttpRequestMessageAsync( Uri.UriSchemeHttp, bufferSize: 64 * 1024, maxHeaderSize: 64 * 1024); Assert.Equal(HttpMethod.Get, httpRequestMessage.Method); Assert.Equal(requestPath, httpRequestMessage.RequestUri.PathAndQuery); Assert.Equal("msdn.microsoft.com", httpRequestMessage.Headers.Host); IEnumerable actualCookieValue; Assert.True(httpRequestMessage.Headers.TryGetValues("Cookie", out actualCookieValue)); Assert.Equal(cookieValue, Assert.Single(actualCookieValue)); } [Theory] [InlineData("")] [InlineData("HTTP/1.1")] [InlineData("HTTP/1.1 200 OK")] [InlineData("HTTP/1.1 200 OK\r\n")] [InlineData("HTTP/1.1 200 OK\r\nServer:")] [InlineData("HTTP/1.1 200 OK\r\nServer: server")] [InlineData("HTTP/1.1 200 OK\r\nServer: server\r\n")] [InlineData("HTTP/1.1 200 OK\r\nServer: server\r\n\r")] public Task ReadAsHttpResponseMessageAsync_IncompleteResponse(string incompleteResponse) { StringContent content = new StringContent(incompleteResponse); content.Headers.ContentType = ParserData.HttpResponseMediaType; return Assert.ThrowsAsync(() => content.ReadAsHttpResponseMessageAsync()); } [Theory] [InlineData("")] [InlineData("GET")] [InlineData("GET / HTTP/1.0")] [InlineData("GET / HTTP/1.0\r\n")] [InlineData("GET / HTTP/1.0\r\nHost:")] [InlineData("GET / HTTP/1.0\r\nHost: localhost")] [InlineData("GET / HTTP/1.0\r\nHost: localhost\r\n")] [InlineData("GET / HTTP/1.0\r\nHost: localhost\r\n\r")] public Task ReadAsHttpRequestMessageAsync_IncompleteRequest(string incompleteRequest) { StringContent content = new StringContent(incompleteRequest); content.Headers.ContentType = ParserData.HttpRequestMediaType; return Assert.ThrowsAsync(() => content.ReadAsHttpRequestMessageAsync()); } [Theory] [PropertyData("ClientRoundTripData")] public async Task RoundtripClientRequest(IEnumerable message) { HttpContent content = CreateContent(true, message, null); HttpRequestMessage httpRequest = await content.ReadAsHttpRequestMessageAsync(); HttpMessageContent httpMessageContent = new HttpMessageContent(httpRequest); MemoryStream destination = new MemoryStream(); await httpMessageContent.CopyToAsync(destination); destination.Seek(0, SeekOrigin.Begin); string destinationMessage = new StreamReader(destination).ReadToEnd(); string sourceMessage = await content.ReadAsStringAsync(); Assert.Equal(sourceMessage, destinationMessage); } [Theory] [PropertyData("ServerRoundTripData")] public async Task RoundtripServerResponse(IEnumerable message) { HttpContent content = CreateContent(false, message, @"Object moved

Object moved to here.

"); HttpResponseMessage httpResponse = await content.ReadAsHttpResponseMessageAsync(); HttpMessageContent httpMessageContent = new HttpMessageContent(httpResponse); MemoryStream destination = new MemoryStream(); await httpMessageContent.CopyToAsync(destination); destination.Seek(0, SeekOrigin.Begin); string destinationMessage = new StreamReader(destination).ReadToEnd(); string sourceMessage = await content.ReadAsStringAsync(); Assert.Equal(sourceMessage, destinationMessage); } [Fact] public Task ReadAsHttpRequestMessageAsync_cancellationToken_PassesCancellationToken() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = CreateContent(isRequest: true, hasEntity: false); return Assert.ThrowsAsync(() => content.ReadAsHttpRequestMessageAsync(cts.Token)); } [Fact] public Task ReadAsHttpRequestMessageAsync_uriScheme_cancellationToken_PassesCancellationToken() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = CreateContent(isRequest: true, hasEntity: false); return Assert.ThrowsAsync( () => content.ReadAsHttpRequestMessageAsync("http", cts.Token)); } [Fact] public Task ReadAsHttpRequestMessageAsync_uriScheme_bufferSize_cancellationToken_PassesCancellationToken() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = CreateContent(isRequest: true, hasEntity: false); return Assert.ThrowsAsync( () => content.ReadAsHttpRequestMessageAsync("http", 1024, cts.Token)); } [Fact] public Task ReadAsHttpRequestMessageAsync_uriScheme_bufferSize_maxHeaderSize_cancellationToken_PassesCancellationToken() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); HttpContent content = CreateContent(isRequest: true, hasEntity: false); return Assert.ThrowsAsync( () => content.ReadAsHttpRequestMessageAsync("http", 1024, 1024, cts.Token)); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpContentMultipartExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class HttpContentMultipartExtensionsTests { private const string ValidBoundary = "-A-"; private const string DefaultContentType = "text/plain"; private const string DefaultContentDisposition = "form-data"; private const string ExceptionStreamProviderMessage = "Bad Stream Provider!"; private const string ExceptionSyncStreamMessage = "Bad Sync Stream!"; private const string ExceptionAsyncStreamMessage = "Bad Async Stream!"; public static TheoryDataSet IsMimeMultipartContentTestData { get { return new TheoryDataSet { { "text/plain", false, "plain", false }, { "application/*", false, "related", false }, { "*/*", false, "related", false }, { "multipart/form-data", false, "form-data", false }, { "multipart/form-data; boundary=1234", true, "related", false }, { "multipart/form-data; boundary=1234; charset=utf-8", true, "form-data", true }, { "Multipart/Related; boundary=example-1; start=\"<950120.aaCC@XIson.com>\"; type=\"Application/X-FixedRecord\"; start-info=\"-o ps\"", true, "related", true }, }; } } public static TheoryDataSet IsMimeMultipartContentTestData_NoSubType { get { var dataSet = new TheoryDataSet(); foreach (var item in IsMimeMultipartContentTestData) { dataSet.Add((string)item[0], (bool)item[1]); } return dataSet; } } private static HttpContent CreateContent(string boundary, params string[] bodyEntity) { return CreateContentWithContentType(boundary, DefaultContentType, bodyEntity); } private static HttpContent CreateContentWithContentType(string boundary, string partContentType, params string[] bodyEntity) { List entities = new List(); int cnt = 0; foreach (var body in bodyEntity) { byte[] header = InternetMessageFormatHeaderParserTests.CreateBuffer( String.Format("N{0}: V{0}", cnt), String.Format("Content-Type: {0}", partContentType), String.Format("Content-Disposition: {0}; FileName=\"N{1}\"", DefaultContentDisposition, cnt)); entities.Add(Encoding.UTF8.GetString(header) + body); cnt++; } byte[] message = MimeMultipartParserTests.CreateBuffer(boundary, entities.ToArray()); HttpContent result = new ByteArrayContent(message); var contentType = new MediaTypeHeaderValue("multipart/form-data"); contentType.Parameters.Add(new NameValueHeaderValue("boundary", String.Format("\"{0}\"", boundary))); result.Headers.ContentType = contentType; return result; } private static async Task ValidateContentsAsync(IEnumerable contents) { int cnt = 0; foreach (var content in contents) { Assert.NotNull(content); Assert.NotNull(content.Headers); Assert.Equal(4, content.Headers.Count()); IEnumerable parsedValues = content.Headers.GetValues(String.Format("N{0}", cnt)); string parsedValue = Assert.Single(parsedValues); Assert.Equal(String.Format("V{0}", cnt), parsedValue); Assert.Equal(DefaultContentType, content.Headers.ContentType.MediaType); Assert.Equal(DefaultContentDisposition, content.Headers.ContentDisposition.DispositionType); Assert.Equal(String.Format("\"N{0}\"", cnt), content.Headers.ContentDisposition.FileName); await AssertContentLengthHeaderValueAsync(content); cnt++; } } private static async Task AssertContentLengthHeaderValueAsync(HttpContent content) { long contentLength = (await content.ReadAsByteArrayAsync()).LongLength; long contentLengthHeaderValue = content.Headers.ContentLength.GetValueOrDefault(); Assert.Equal(contentLength, contentLengthHeaderValue); } [Fact] public void IsMimeMultipartContent_ThrowsOnNullContent() { Assert.ThrowsArgumentNull(() => HttpContentMultipartExtensions.IsMimeMultipartContent(null), "content"); } [Fact] public void IsMimeMultipartContent_ThrowsOnNullSubType() { StringContent content = new StringContent(String.Empty); Assert.ThrowsArgumentNull(() => HttpContentMultipartExtensions.IsMimeMultipartContent(content, null), "subtype"); } [Theory] [PropertyData("IsMimeMultipartContentTestData")] public void IsMimeMultipartContent_ReturnsCorrectValue(string mediaType, bool isMultipart, string subtype, bool hasSubtype) { StringContent content = new StringContent(String.Empty); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); Assert.Equal(isMultipart, content.IsMimeMultipartContent()); Assert.Equal(hasSubtype, content.IsMimeMultipartContent(subtype)); } [Fact] public Task ReadAsMultipartAsync_ThrowsOnNullStreamProvider() { HttpContent content = CreateContent(ValidBoundary); return Assert.ThrowsArgumentNullAsync(() => content.ReadAsMultipartAsync((MultipartStreamProvider)null), "streamProvider"); } [Fact] public Task ReadAsMultipartAsync_ThrowsOnInvalidBufferSize() { HttpContent content = CreateContent(ValidBoundary); return Assert.ThrowsArgumentGreaterThanOrEqualToAsync( () => content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider(), ParserData.MinBufferSize - 1), "bufferSize", ParserData.MinBufferSize.ToString(), ParserData.MinBufferSize - 1); } [Theory] [PropertyData("IsMimeMultipartContentTestData_NoSubType")] public async Task ReadAsMultipartAsync_DetectsNonMultipartContent(string mediaType, bool isMultipart) { StringContent content = new StringContent(String.Empty); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); if (!isMultipart) { await Assert.ThrowsArgumentAsync(() => content.ReadAsMultipartAsync(), "content"); } } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public async Task ReadAsMultipartAsync_ParsesContent(string boundary) { HttpContent successContent; MultipartMemoryStreamProvider result; successContent = CreateContent(boundary, "A", "B", "C"); result = await successContent.ReadAsMultipartAsync(); Assert.Equal(3, result.Contents.Count); successContent = CreateContent(boundary, "A", "B", "C"); result = await successContent.ReadAsMultipartAsync(new MultipartMemoryStreamProvider()); Assert.Equal(3, result.Contents.Count); successContent = CreateContent(boundary, "A", "B", "C"); result = await successContent.ReadAsMultipartAsync(new MultipartMemoryStreamProvider(), 1024); Assert.Equal(3, result.Contents.Count); } [Fact] public async Task ReadAsMultipartAsync_SkipsHeaderValidation() { // Arrange var content = CreateContentWithContentType("--boundary", "invalid", "SomeContent"); // Act var result = await content.ReadAsMultipartAsync(CancellationToken.None); // Assert var bodyPart = Assert.Single(result.Contents); Assert.Null(bodyPart.Headers.ContentType); Assert.Equal("invalid", Assert.Single(bodyPart.Headers.GetValues("Content-Type"))); } [Fact] public async Task ReadAsMultipartAsync_SetsStronglyTypedHeader_WhenHeaderIsValid() { // Arrange var content = CreateContentWithContentType("--boundary", "application/json", "SomeContent"); // Act var result = await content.ReadAsMultipartAsync(CancellationToken.None); // Assert var bodyPart = Assert.Single(result.Contents); Assert.NotNull(bodyPart.Headers.ContentType); Assert.Equal("application/json", bodyPart.Headers.ContentType.MediaType); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public async Task ReadAsMultipartAsync_ParsesEmptyContent(string boundary) { HttpContent content = CreateContent(boundary); MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(); Assert.Empty(result.Contents); } [Fact] public async Task ReadAsMultipartAsync_ThrowsOnBadStreamProvider() { HttpContent content = CreateContent(ValidBoundary, "A", "B", "C"); IOException exception = await Assert.ThrowsAsync(() => content.ReadAsMultipartAsync(new BadStreamProvider())); InvalidOperationException invalidOperationException = exception.InnerException as InvalidOperationException; Assert.NotNull(invalidOperationException); Assert.NotNull(invalidOperationException.InnerException); Assert.Equal(ExceptionStreamProviderMessage, invalidOperationException.InnerException.Message); } [Fact] public async Task ReadAsMultipartAsync_ThrowsOnNullProvider() { HttpContent content = CreateContent(ValidBoundary, "A", "B", "C"); IOException exception = await Assert.ThrowsAsync(() => content.ReadAsMultipartAsync(new NullProvider())); Assert.IsType(exception.InnerException); } [Fact] public async Task ReadAsMultipartAsync_ThrowsOnReadOnlyStream() { HttpContent content = CreateContent(ValidBoundary, "A", "B", "C"); IOException exception = await Assert.ThrowsAsync(() => content.ReadAsMultipartAsync(new ReadOnlyStreamProvider())); Assert.IsType(exception.InnerException); } [Fact] public Task ReadAsMultipartAsync_ThrowsOnPrematureEndOfStream() { HttpContent content = new StreamContent(Stream.Null); string mediaType = String.Format("multipart/form-data; boundary=\"{0}\"", ValidBoundary); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); return Assert.ThrowsAsync(() => content.ReadAsMultipartAsync()); } [Fact] public async Task ReadAsMultipartAsync_ThrowsOnReadError() { HttpContent content = new StreamContent(new ReadErrorStream()); string mediaType = String.Format("multipart/form-data; boundary=\"{0}\"", ValidBoundary); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); IOException exception = await Assert.ThrowsAsync(() => content.ReadAsMultipartAsync()); Assert.NotNull(exception.InnerException); Assert.Equal(ExceptionAsyncStreamMessage, exception.InnerException.Message); } [Fact] public async Task ReadAsMultipartAsync_ThrowsOnWriteError() { HttpContent content = CreateContent(ValidBoundary, "A", "B", "C"); IOException exception = await Assert.ThrowsAsync(() => content.ReadAsMultipartAsync(new WriteErrorStreamProvider())); Assert.NotNull(exception.InnerException); Assert.Equal(ExceptionAsyncStreamMessage, exception.InnerException.Message); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "SingleShortBodies")] public async Task ReadAsMultipartAsync_SingleShortBodyPart(string boundary, string singleShortBody) { HttpContent content = CreateContent(boundary, singleShortBody); MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(); HttpContent resultContent = Assert.Single(result.Contents); Assert.Equal(singleShortBody, await resultContent.ReadAsStringAsync()); await ValidateContentsAsync(result.Contents); } [Fact] public async Task ReadAsMultipartAsync_WithHugeBody_AvoidStackOverflow() { // Arrange var fiftyMegs = 1024 * 1024 * 50; HttpContent content = CreateContent("---3123---", new string('x', fiftyMegs)); // Act MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider(), 256); // Assert // this is for sanity. The actual test here is that the Act part did not cause a stack overflow Assert.Equal(fiftyMegs, (await result.Contents[0].ReadAsStringAsync()).Length); await ValidateContentsAsync(result.Contents); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "MultipleShortBodies")] public async Task ReadAsMultipartAsync_MultipleShortBodyParts(string boundary, string[] multipleShortBodies) { HttpContent content = CreateContent(boundary, multipleShortBodies); MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(); Assert.Equal(multipleShortBodies.Length, result.Contents.Count); for (var check = 0; check < multipleShortBodies.Length; check++) { Assert.Equal(multipleShortBodies[check], await result.Contents[check].ReadAsStringAsync()); } await ValidateContentsAsync(result.Contents); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "SingleLongBodies")] public async Task ReadAsMultipartAsync_SingleLongBodyPart(string boundary, string singleLongBody) { HttpContent content = CreateContent(boundary, singleLongBody); MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(); HttpContent resultContent = Assert.Single(result.Contents); Assert.Equal(singleLongBody, await resultContent.ReadAsStringAsync()); await ValidateContentsAsync(result.Contents); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries", typeof(MimeMultipartParserTests), "MultipleLongBodies")] public async Task ReadAsMultipartAsync_MultipleLongBodyParts(string boundary, string[] multipleLongBodies) { HttpContent content = CreateContent(boundary, multipleLongBodies); MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider(), ParserData.MinBufferSize); Assert.Equal(multipleLongBodies.Length, result.Contents.Count); for (var check = 0; check < multipleLongBodies.Length; check++) { Assert.Equal(multipleLongBodies[check], await result.Contents[check].ReadAsStringAsync()); } await ValidateContentsAsync(result.Contents); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public async Task ReadAsMultipartAsync_UsingMultipartContent(string boundary) { MultipartContent content = new MultipartContent("mixed", boundary); content.Add(new StringContent("A")); content.Add(new StringContent("B")); content.Add(new StringContent("C")); MemoryStream memStream = new MemoryStream(); await content.CopyToAsync(memStream); memStream.Position = 0; byte[] data = memStream.ToArray(); var byteContent = new ByteArrayContent(data); byteContent.Headers.ContentType = content.Headers.ContentType; MultipartMemoryStreamProvider result = await byteContent.ReadAsMultipartAsync(); Assert.Equal(3, result.Contents.Count); Assert.Equal("A", await result.Contents[0].ReadAsStringAsync()); Assert.Equal("B", await result.Contents[1].ReadAsStringAsync()); Assert.Equal("C", await result.Contents[2].ReadAsStringAsync()); } [Theory] [TestDataSet(typeof(MimeMultipartParserTests), "Boundaries")] public async Task ReadAsMultipartAsync_NestedMultipartContent(string boundary) { const int nesting = 10; const string innerText = "Content"; MultipartContent innerContent = new MultipartContent("mixed", boundary); innerContent.Add(new StringContent(innerText)); for (var cnt = 0; cnt < nesting; cnt++) { string outerBoundary = String.Format("{0}_{1}", boundary, cnt); MultipartContent outerContent = new MultipartContent("mixed", outerBoundary); outerContent.Add(innerContent); innerContent = outerContent; } MemoryStream memStream = new MemoryStream(); await innerContent.CopyToAsync(memStream); memStream.Position = 0; byte[] data = memStream.ToArray(); HttpContent content = new ByteArrayContent(data); content.Headers.ContentType = innerContent.Headers.ContentType; for (var cnt = 0; cnt < nesting + 1; cnt++) { MultipartMemoryStreamProvider result = await content.ReadAsMultipartAsync(); content = Assert.Single(result.Contents); Assert.NotNull(content); } string text = await content.ReadAsStringAsync(); Assert.Equal(innerText, text); } [Fact] public async Task ReadAsMultipartAsyncOfT_PassesCancellationToken() { CancellationToken token = new CancellationToken(); HttpContent content = CreateContent("boundary"); Mock provider = new Mock(); provider.Setup(p => p.ExecutePostProcessingAsync(token)) .Returns(Task.FromResult(42)) .Verifiable(); await content.ReadAsMultipartAsync(provider.Object, token); provider.Verify(); } public class ReadOnlyStream : MemoryStream { public override bool CanWrite { get { return false; } } } public class ReadErrorStream : MemoryStream { public override int Read(byte[] buffer, int offset, int count) { throw new IOException(ExceptionSyncStreamMessage); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { throw new IOException(ExceptionAsyncStreamMessage); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new IOException(ExceptionAsyncStreamMessage); } #endif } public class WriteErrorStream : MemoryStream { public override void Write(byte[] buffer, int offset, int count) { throw new IOException(ExceptionSyncStreamMessage); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { throw new IOException(ExceptionAsyncStreamMessage); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new IOException(ExceptionAsyncStreamMessage); } #endif } public class BadStreamProvider : MultipartStreamProvider { public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { throw new Exception(ExceptionStreamProviderMessage); } } public class NullProvider : MultipartStreamProvider { public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { return null; } } public class ReadOnlyStreamProvider : MultipartStreamProvider { public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { return new ReadOnlyStream(); } } public class WriteErrorStreamProvider : MultipartStreamProvider { public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { return new WriteErrorStream(); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpHeaderExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpHeaderExtensionsTest { [Fact] public void CopyTo_CopiesContentHeaders() { // Arrange HttpContentHeaders source = FormattingUtilities.CreateEmptyContentHeaders(); source.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf8; parameter=value"); source.ContentLength = 1234; source.ContentLocation = new Uri("http://some.host"); source.Add("test-name1", "test-value1"); source.Add("test-name2", "test-value2"); HttpContentHeaders destination = FormattingUtilities.CreateEmptyContentHeaders(); // Act source.CopyTo(destination); // Assert Assert.Equal(source.ToString(), destination.ToString()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpMessageContentTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpMessageContentTests { private static readonly int iterations = 5; private static void AddMessageHeaders(HttpHeaders headers) { headers.Add("N1", new string[] { "V1a", "V1b", "V1c", "V1d", "V1e" }); headers.Add("N2", "V2"); } private static HttpRequestMessage CreateRequest(Uri requestUri, bool containsEntity) { HttpRequestMessage httpRequest = new HttpRequestMessage(); httpRequest.Method = new HttpMethod(ParserData.HttpMethod); httpRequest.RequestUri = requestUri; httpRequest.Version = new Version("1.2"); AddMessageHeaders(httpRequest.Headers); if (containsEntity) { httpRequest.Content = new StringContent(ParserData.HttpMessageEntity); } return httpRequest; } private static HttpResponseMessage CreateResponse(bool containsEntity) { HttpResponseMessage httpResponse = new HttpResponseMessage(); httpResponse.StatusCode = ParserData.HttpStatus; httpResponse.ReasonPhrase = ParserData.HttpReasonPhrase; httpResponse.Version = new Version("1.2"); AddMessageHeaders(httpResponse.Headers); httpResponse.Content = containsEntity ? new StringContent(ParserData.HttpMessageEntity) : new StreamContent(Stream.Null); return httpResponse; } private static async Task ReadContentAsync(HttpContent content, bool unBuffered = false) { if (unBuffered) { var stream = new MemoryStream(); await content.CopyToAsync(stream); stream.Position = 0L; // StreamReader will dispose of the Stream. using var reader = new StreamReader(stream); return await reader.ReadToEndAsync(); } else { await content.LoadIntoBufferAsync(); return await content.ReadAsStringAsync(); } } private static async Task ValidateRequest(HttpContent content, bool containsEntity, bool unBuffered = false) { Assert.Equal(ParserData.HttpRequestMediaType, content.Headers.ContentType); long? length = content.Headers.ContentLength; Assert.NotNull(length); string message = await ReadContentAsync(content, unBuffered); if (containsEntity) { Assert.Equal(ParserData.HttpRequestWithEntity.Length, length); Assert.Equal(ParserData.HttpRequestWithEntity, message); } else { Assert.Equal(ParserData.HttpRequest.Length, length); Assert.Equal(ParserData.HttpRequest, message); } } private static async Task ValidateResponse(HttpContent content, bool containsEntity, bool unBuffered = false) { Assert.Equal(ParserData.HttpResponseMediaType, content.Headers.ContentType); long? length = content.Headers.ContentLength; Assert.NotNull(length); string message = await ReadContentAsync(content, unBuffered); if (containsEntity) { Assert.Equal(ParserData.HttpResponseWithEntity.Length, length); Assert.Equal(ParserData.HttpResponseWithEntity, message); } else { Assert.Equal(ParserData.HttpResponse.Length, length); Assert.Equal(ParserData.HttpResponse, message); } } [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(TypeAssert.TypeProperties.IsPublicVisibleClass | TypeAssert.TypeProperties.IsDisposable); } [Fact] public void RequestConstructor() { HttpRequestMessage request = new HttpRequestMessage(); HttpMessageContent instance = new HttpMessageContent(request); Assert.NotNull(instance); Assert.Same(request, instance.HttpRequestMessage); Assert.Null(instance.HttpResponseMessage); } [Fact] public void RequestConstructorThrowsOnNull() { Assert.ThrowsArgumentNull(() => { new HttpMessageContent((HttpRequestMessage)null); }, "httpRequest"); } [Fact] public void ResponseConstructor() { HttpResponseMessage response = new HttpResponseMessage(); HttpMessageContent instance = new HttpMessageContent(response); Assert.NotNull(instance); Assert.Same(response, instance.HttpResponseMessage); Assert.Null(instance.HttpRequestMessage); } [Fact] public void ResponseConstructorThrowsOnNull() { Assert.ThrowsArgumentNull(() => { new HttpMessageContent((HttpResponseMessage)null); }, "httpResponse"); } [Fact] public async Task SerializeRequest() { for (int cnt = 0; cnt < iterations; cnt++) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, false); HttpMessageContent instance = new HttpMessageContent(request); await ValidateRequest(instance, false); } } [Fact] public async Task SerializeRequestWithExistingHostHeader() { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, false); string host = ParserData.HttpHostName; request.Headers.Host = host; HttpMessageContent instance = new HttpMessageContent(request); string message = await ReadContentAsync(instance); Assert.Equal(ParserData.HttpRequestWithHost, message); } [Theory] [InlineData(false)] [InlineData(true)] public async Task SerializeRequestMultipleTimes(bool unBuffered) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); HttpMessageContent instance = new(request); for (int cnt = 0; cnt < iterations; cnt++) { await ValidateRequest(instance, containsEntity: false, unBuffered); } } [Fact] public async Task SerializeResponse() { for (int cnt = 0; cnt < iterations; cnt++) { HttpResponseMessage response = CreateResponse(false); HttpMessageContent instance = new HttpMessageContent(response); await ValidateResponse(instance, false); } } [Theory] [InlineData(false)] [InlineData(true)] public async Task SerializeResponseMultipleTimes(bool unBuffered) { HttpResponseMessage response = CreateResponse(containsEntity: false); HttpMessageContent instance = new(response); for (int cnt = 0; cnt < iterations; cnt++) { await ValidateResponse(instance, containsEntity: false, unBuffered); } } [Fact] public async Task SerializeRequestWithEntity() { for (int cnt = 0; cnt < iterations; cnt++) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, true); HttpMessageContent instance = new HttpMessageContent(request); await ValidateRequest(instance, true); } } [Theory] [InlineData(false)] [InlineData(true)] public async Task SerializeRequestWithEntityMultipleTimes(bool unBuffered) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, containsEntity: true); HttpMessageContent instance = new(request); for (int cnt = 0; cnt < iterations; cnt++) { await ValidateRequest(instance, containsEntity: true, unBuffered); } } [Fact] public async Task SerializeResponseWithEntity() { for (int cnt = 0; cnt < iterations; cnt++) { HttpResponseMessage response = CreateResponse(true); HttpMessageContent instance = new HttpMessageContent(response); await ValidateResponse(instance, true); } } [Theory] [InlineData(false)] [InlineData(true)] public async Task SerializeResponseWithEntityMultipleTimes(bool unBuffered) { HttpResponseMessage response = CreateResponse(containsEntity: true); HttpMessageContent instance = new(response); for (int cnt = 0; cnt < iterations; cnt++) { await ValidateResponse(instance, containsEntity: true, unBuffered); } } [Fact] public async Task SerializeRequestAsync() { for (int cnt = 0; cnt < iterations; cnt++) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, false); HttpMessageContent instance = new HttpMessageContent(request); await ValidateRequest(instance, false); } } [Fact] public async Task SerializeResponseAsync() { for (int cnt = 0; cnt < iterations; cnt++) { HttpResponseMessage response = CreateResponse(false); HttpMessageContent instance = new HttpMessageContent(response); await ValidateResponse(instance, false); } } [Fact] public async Task SerializeRequestWithPortAndQueryAsync() { for (int cnt = 0; cnt < iterations; cnt++) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUriWithPortAndQuery, false); HttpMessageContent instance = new HttpMessageContent(request); string message = await ReadContentAsync(instance); Assert.Equal(ParserData.HttpRequestWithPortAndQuery, message); } } [Fact] public async Task SerializeRequestWithEntityAsync() { for (int cnt = 0; cnt < iterations; cnt++) { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, true); HttpMessageContent instance = new HttpMessageContent(request); await ValidateRequest(instance, true); } } [Fact] public async Task SerializeResponseWithEntityAsync() { for (int cnt = 0; cnt < iterations; cnt++) { HttpResponseMessage response = CreateResponse(true); HttpMessageContent instance = new HttpMessageContent(response); await ValidateResponse(instance, true); } } [Fact] public void DisposeInnerHttpRequestMessage() { HttpRequestMessage request = CreateRequest(ParserData.HttpRequestUri, false); HttpMessageContent instance = new HttpMessageContent(request); instance.Dispose(); Assert.ThrowsObjectDisposed(() => { request.Method = HttpMethod.Get; }, typeof(HttpRequestMessage).FullName); } [Fact] public void DisposeInnerHttpResponseMessage() { HttpResponseMessage response = CreateResponse(false); HttpMessageContent instance = new HttpMessageContent(response); instance.Dispose(); Assert.ThrowsObjectDisposed(() => { response.StatusCode = HttpStatusCode.OK; }, typeof(HttpResponseMessage).FullName); } [Fact] public void Request_ContentLengthNull_IfReadOnlyStream() { var request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); request.Content = new StreamContent(new ReadOnlyStream()); var instance = new HttpMessageContent(request); var length = instance.Headers.ContentLength; Assert.Null(length); } [Fact] public void Response_ContentLengthNull_IfReadOnlyStream() { var response = CreateResponse(containsEntity: false); response.Content = new StreamContent(new ReadOnlyStream()); var instance = new HttpMessageContent(response); var length = instance.Headers.ContentLength; Assert.Null(length); } // Also confirms content can be serialized multiple times if either buffered or involves a seekable Stream. [Theory] [InlineData(false, true)] [InlineData(true, false)] public async Task Request_NoContentLength_IfNotRequested(bool readOnlyStream, bool unBuffered) { var request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); if (readOnlyStream) { request.Content = new StreamContent(new ReadOnlyStream()); } var instance = new HttpMessageContent(request); for (int cnt = 0; cnt < iterations; cnt++) { var contentString = await ReadContentAsync(instance, unBuffered); Assert.Equal(ParserData.HttpRequest.Replace("Content-Length: 0\r\n", ""), contentString); } } // Also confirms content can be serialized multiple times if either buffered or involves a seekable Stream. [Theory] [InlineData(false, true)] [InlineData(true, false)] public async Task Response_NoContentLength_IfNotRequested(bool readOnlyStream, bool unBuffered) { var response = CreateResponse(containsEntity: false); if (readOnlyStream) { response.Content = new StreamContent(new ReadOnlyStream()); } var instance = new HttpMessageContent(response); for (int cnt = 0; cnt < iterations; cnt++) { var contentString = await ReadContentAsync(instance, unBuffered); Assert.Equal(ParserData.HttpResponse.Replace("Content-Length: 0\r\n", ""), contentString); } } // Covers the false, false case of Request_NoContentLength_IfNotRequested(...). [Fact] public async Task Request_HasContentLength_IfBuffered_EvenIfNotRequested() { var request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); var instance = new HttpMessageContent(request); for (int cnt = 0; cnt < iterations; cnt++) { var contentString = await ReadContentAsync(instance, unBuffered: false); Assert.Equal(ParserData.HttpRequest, contentString); } } // Covers the false, false case of Response_NoContentLength_IfNotRequested(...). [Fact] public async Task Response_HasContentLength_IfBuffered_EvenIfNotRequested() { var response = CreateResponse(containsEntity: false); var instance = new HttpMessageContent(response); for (int cnt = 0; cnt < iterations; cnt++) { var contentString = await ReadContentAsync(instance, unBuffered: false); Assert.Equal(ParserData.HttpResponse, contentString); } } // Covers the true, true case of Request_NoContentLength_IfNotRequested(...). [Fact] public async Task Request_CannotSerializeMultipleTimes_IfNotBufferedAndNotSeekable() { var request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); request.Content = new StreamContent(new ReadOnlyStream()); var instance = new HttpMessageContent(request); // Act #1 var contentString = await ReadContentAsync(instance, unBuffered: true); // Assert #1 Assert.Equal(ParserData.HttpRequest.Replace("Content-Length: 0\r\n", ""), contentString); // Act #2 await Assert.ThrowsAsync( () => ReadContentAsync(instance, unBuffered: true), "The 'HttpContent' of the 'HttpRequestMessage' has already been read."); } // Covers the true, true case of Response_NoContentLength_IfNotRequested(...). [Fact] public async Task Response_CannotSerializeMultipleTimes_IfNotBufferedAndNotSeekable() { var response = CreateResponse(containsEntity: false); response.Content = new StreamContent(new ReadOnlyStream()); var instance = new HttpMessageContent(response); // Act #1 var contentString = await ReadContentAsync(instance, unBuffered: true); // Assert #1 Assert.Equal(ParserData.HttpResponse.Replace("Content-Length: 0\r\n", ""), contentString); // Act #2 await Assert.ThrowsAsync( () => ReadContentAsync(instance, unBuffered: true), "The 'HttpContent' of the 'HttpResponseMessage' has already been read."); } [Theory] [InlineData(false)] [InlineData(true)] public async Task Request_CannotSerialize_IfWriteOnlyStream(bool unBuffered) { var request = CreateRequest(ParserData.HttpRequestUri, containsEntity: false); request.Content = new StreamContent(new WriteOnlyStream()); var instance = new HttpMessageContent(request); await Assert.ThrowsAsync( () => ReadContentAsync(instance, unBuffered), "Stream does not support reading."); } [Theory] [InlineData(false)] [InlineData(true)] public async Task Response_CannotSerialize_IfWriteOnlyStream(bool unBuffered) { var response = CreateResponse(containsEntity: false); response.Content = new StreamContent(new WriteOnlyStream()); var instance = new HttpMessageContent(response); await Assert.ThrowsAsync( () => ReadContentAsync(instance, unBuffered), "Stream does not support reading."); } // Unlike Stream.Null, this stream does not support seeking. Bit more like (say) a network stream or // the EmptyReadStream introduced in .NET 5. Note: EmptyReadStream should never be visible to our code // because HttpContentMessageExtensions and HttpRequestMessageExtensions overwrite // HttpResponseMessage.Content (or HttpRequestMessage.Content in one case) when creating an instance. private class ReadOnlyStream : Stream { public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotImplementedException(); public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override void Flush() { // Do nothing. } public override int Read(byte[] buffer, int offset, int count) => 0; public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); public override void SetLength(long value) => throw new NotImplementedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); } // Unlike Stream.Null, this stream does not support seeking. Bit more like (say) a network stream. private class WriteOnlyStream : Stream { public override bool CanRead => false; public override bool CanSeek => false; public override bool CanWrite => true; public override long Length => throw new NotImplementedException(); public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override void Flush() { // Do nothing. } public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException(); public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException(); public override void SetLength(long value) => throw new NotImplementedException(); public override void Write(byte[] buffer, int offset, int count) { // Ignore all parameters and do nothing. } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpRequestHeadersExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpRequestHeadersExtensionsTest { public static TheoryDataSet CookieMatches { get { // IEnumerable inputCookies, string matchName, IEnumerable expectedOuput return new TheoryDataSet { { new string[] {}, "empty", new string[] {} }, { new string[] { "RMID=2dab5fc9747d4f8edaf410ff", "adxcs=-", "adxcl=l*2ba62=4fc449bf:1", "adxcs=si=0:1", }, "nomatch", new string[] {} }, { new string[] { "RMID=2dab5fc9747d4f8edaf410ff", "adxcs=-", "adxcl=l*2ba62=4fc449bf:1", "ADXCS=si=0:1", }, "adxcs", new string[] { "adxcs=-", "ADXCS=si=0%3A1" } }, { new string[] { "MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!", "MC0=1334766377159; MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!; MUID=20EC57A324256BF3039D54E520256B7D&TUID=1", }, "A", new string[] { "MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!", "MC0=1334766377159; MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!; MUID=20EC57A324256BF3039D54E520256B7D&TUID=1", } }, { new string[] { "MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!", "MC0=1334766377159; MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!; MUID=20EC57A324256BF3039D54E520256B7D&TUID=1", }, "MC0", new string[] { "MC0=1334766377159; MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!; MUID=20EC57A324256BF3039D54E520256B7D&TUID=1", } }, { new string[] { "MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!", "MC0=1334766377159; MC1=GUID=e87574286c55d547b5a0b19fb27d57a4&HASH=2874&LV=20124&V=3&LU=1334766376863; MS0=7bbaad2a8316483c89bbd2ca4e96fcea; A=I&I=AxUFAAAAAACSCAAAHFNnP3xE7Uth5BCZZSiqZQ!!; MUID=20EC57A324256BF3039D54E520256B7D&TUID=1", }, "MC", new string[] { } }, }; } } [Fact] public void GetCookies_ThrowsOnNull() { Assert.ThrowsArgumentNull(() => HttpRequestHeadersExtensions.GetCookies(null), "headers"); } [Fact] public void GetCookies_GetsCookiesReturnsEmptyCollection() { // Arrange HttpRequestHeaders headers = CreateHttpRequestHeaders(); // Act IEnumerable cookies = headers.GetCookies(); // Assert Assert.Empty(cookies); } [Theory] [InlineData("name1=n1=v1&n2=v2&n3=v3; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly")] public void GetCookies_GetsCookies(string expectedCookie) { // Arrange HttpRequestHeaders headers = CreateHttpRequestHeaders(); headers.TryAddWithoutValidation("Cookie", expectedCookie); // Act IEnumerable cookies = headers.GetCookies(); // Assert CookieHeaderValue cookie = Assert.Single(cookies); string actualCookie = cookie.ToString(); Assert.Equal(expectedCookie, actualCookie); } [Fact] public void GetCookiesByName_ThrowsOnNullHeaders() { Assert.ThrowsArgumentNull(() => HttpRequestHeadersExtensions.GetCookies(null, "empty"), "headers"); } [Fact] public void GetCookiesByName_ThrowsOnNullName() { HttpRequestHeaders headers = CreateHttpRequestHeaders(); Assert.ThrowsArgumentNull(() => HttpRequestHeadersExtensions.GetCookies(headers, null), "name"); } [Theory] [PropertyData("CookieMatches")] public void GetCookiesByName_GetsCookies(IEnumerable cookies, string name, IEnumerable expectedCookies) { // Arrange HttpRequestHeaders headers = CreateHttpRequestHeaders(); foreach (string cookie in cookies) { headers.TryAddWithoutValidation("Cookie", cookie); } // Act IEnumerable actualCookieHeaderValues = headers.GetCookies(name); // Assert IEnumerable actualCookies = actualCookieHeaderValues.Select(c => c.ToString()); Assert.True(actualCookies.SequenceEqual(expectedCookies)); } private static HttpRequestHeaders CreateHttpRequestHeaders() { HttpRequestMessage request = new HttpRequestMessage(); return request.Headers; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpRequestMessageCommonExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http { public class HttpRequestMessageExtensionsTest { [Fact] public void IsCorrectType() { Assert.Type.HasProperties(typeof(HttpRequestMessageExtensions), TypeAssert.TypeProperties.IsStatic | TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void CreateResponseThrowsOnNull() { Assert.ThrowsArgumentNull(() => HttpRequestMessageExtensions.CreateResponse(null), "request"); } [Fact] public void CreateResponseWithStatusThrowsOnNull() { Assert.ThrowsArgumentNull(() => HttpRequestMessageExtensions.CreateResponse(null, HttpStatusCode.OK), "request"); } [Fact] public void CreateResponse() { // Arrange HttpRequestMessage request = new HttpRequestMessage(); // Act HttpResponseMessage response = request.CreateResponse(); // Assert Assert.Same(request, response.RequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact] public void CreateResponseWithStatus() { // Arrange HttpRequestMessage request = new HttpRequestMessage(); // Act HttpResponseMessage response = request.CreateResponse(HttpStatusCode.NotImplemented); // Assert Assert.Same(request, response.RequestMessage); Assert.Equal(HttpStatusCode.NotImplemented, response.StatusCode); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpResponseHeadersExtensionsTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class HttpResponseHeadersExtensionsTest { [Fact] public void AddCookies_ThrowsOnNull() { HttpResponseHeaders headers = CreateHttpResponseHeaders(); List cookies = new List(); Assert.ThrowsArgumentNull(() => HttpResponseHeadersExtensions.AddCookies(null, cookies), "headers"); Assert.ThrowsArgumentNull(() => HttpResponseHeadersExtensions.AddCookies(headers, null), "cookies"); } [Fact] public void AddCookies_ThrowsOnNullCookie() { HttpResponseHeaders headers = CreateHttpResponseHeaders(); List cookies = new List(); cookies.Add(null); Assert.ThrowsArgument(() => HttpResponseHeadersExtensions.AddCookies(headers, cookies), "cookies"); } [Theory] [InlineData("name1=n1=v1&n2=v2&n3=v3; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly")] public void AddCookies_AddsCookies(string expectedCookie) { // Arrange HttpResponseHeaders headers = CreateHttpResponseHeaders(); List cookies = new List(); CookieHeaderValue cookie; bool parsedCorrectly = CookieHeaderValue.TryParse(expectedCookie, out cookie); cookies.Add(cookie); // Act headers.AddCookies(cookies); // Assert Assert.True(parsedCorrectly); IEnumerable actualCookies; bool addedCorrectly = headers.TryGetValues("Set-Cookie", out actualCookies); Assert.True(addedCorrectly); string actualCookie = Assert.Single(actualCookies); Assert.Equal(expectedCookie, actualCookie); } private static HttpResponseHeaders CreateHttpResponseHeaders() { HttpResponseMessage response = new HttpResponseMessage(); return response.Headers; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpUnsortedRequestTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http { public class HttpUnsortedRequestTest { [Fact] public void Constructor_InitializesHeaders() { HttpUnsortedRequest request = new HttpUnsortedRequest(); Assert.IsType(request.HttpHeaders); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/HttpUnsortedResponseTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http { public class HttpUnsortedResponseTest { [Fact] public void Constructor_InitializesHeaders() { HttpUnsortedResponse response = new HttpUnsortedResponse(); Assert.IsType(response.HttpHeaders); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/AsyncResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Internal; using Microsoft.TestCommon; using Moq; namespace System.Web.Http { public class AsyncResultTest { private static readonly TimeSpan _timeout = TimeSpan.FromMilliseconds(10); [Fact] public void Constructor_Initializes() { // Arrange AsyncCallback callback = new AsyncCallback(_ => { }); object state = new object(); // Act MockAsyncResult mockAsyncResult = new MockAsyncResult(callback, state); // Assert Assert.True(mockAsyncResult.HasCallback); Assert.False(mockAsyncResult.IsCompleted); Assert.False(mockAsyncResult.CompletedSynchronously); Assert.Same(state, mockAsyncResult.AsyncState); } [Theory] [InlineData(true)] [InlineData(false)] public void Complete_SetsCompletedSynchronously(bool completedSynchronously) { // Arrange MockAsyncResult mockAsyncResult = new MockAsyncResult(null, null); // Act mockAsyncResult.Complete(completedSynchronously); // Assert Assert.Equal(completedSynchronously, mockAsyncResult.CompletedSynchronously); } [Theory] [InlineData(true)] [InlineData(false)] public void Complete_SetsIsCompleted(bool completedSynchronously) { // Arrange MockAsyncResult mockAsyncResult = new MockAsyncResult(null, null); // Act mockAsyncResult.Complete(completedSynchronously); // Assert Assert.True(mockAsyncResult.IsCompleted); } [Theory] [InlineData(true)] [InlineData(false)] public void Complete_CallsCallback(bool completedSynchronously) { // Arrange MockAsyncCallback mockCallback = new MockAsyncCallback(false); MockAsyncResult mockAsyncResult = new MockAsyncResult(mockCallback.Callback, null); // Act mockAsyncResult.Complete(completedSynchronously); // Assert Assert.True(mockCallback.WasInvoked); Assert.Same(mockAsyncResult, mockCallback.AsyncResult); } [Theory] [InlineData(true)] [InlineData(false)] public void Complete_ThrowsOnMultipleCompletes(bool completedSynchronously) { // Arrange MockAsyncResult mockAsyncResult = new MockAsyncResult(null, null); // Act mockAsyncResult.Complete(completedSynchronously); // Assert Assert.Throws(() => mockAsyncResult.Complete(completedSynchronously)); } [Fact] public void Complete_ThrowsIfCallbackThrows() { // Arrange MockAsyncCallback mockCallback = new MockAsyncCallback(true); MockAsyncResult mockAsyncResult = new MockAsyncResult(mockCallback.Callback, null); // Act/Assert Assert.Throws(() => mockAsyncResult.Complete(false)); } [Fact] public void End_ThrowsOnNullAsyncResult() { Assert.ThrowsArgumentNull(() => MockAsyncResult.End(null), "result"); } [Fact] public void End_ThrowsOnInvalidAsyncResult() { Mock mockIAsyncResult = new Mock(); Assert.ThrowsArgument(() => MockAsyncResult.End(mockIAsyncResult.Object), "result"); } [Theory] [InlineData(true)] [InlineData(false)] public void End_ThrowsIfCalledTwiceOnSameAsyncResult(bool completedSynchronously) { // Arrange MockAsyncCallback mockCallback = new MockAsyncCallback(false); MockAsyncResult mockAsyncResult = new MockAsyncResult(mockCallback.Callback, null); mockAsyncResult.Complete(completedSynchronously); // Act MockAsyncResult.End(mockAsyncResult); // Act Assert.Throws(() => MockAsyncResult.End(mockAsyncResult)); } [Theory] [InlineData(true)] [InlineData(false)] public void End_ThrowsIfCompletedWithException(bool completedSynchronously) { // Arrange MockAsyncCallback mockCallback = new MockAsyncCallback(false); MockAsyncResult mockAsyncResult = new MockAsyncResult(mockCallback.Callback, null); ApplicationException applicationException = new ApplicationException("Complete failed!"); mockAsyncResult.Complete(completedSynchronously, applicationException); // Act/Assert Assert.Throws(() => MockAsyncResult.End(mockAsyncResult)); } internal class MockAsyncResult : AsyncResult { public MockAsyncResult(AsyncCallback callback, object state) : base(callback, state) { } public new void Complete(bool completedSynchronously) { base.Complete(completedSynchronously); } public new void Complete(bool completedSynchronously, Exception e) { base.Complete(completedSynchronously, e); } public static new TAsyncResult End(IAsyncResult result) where TAsyncResult : AsyncResult { return AsyncResult.End(result); } } public class MockAsyncCallback { private bool _throwInCallback; public MockAsyncCallback(bool throwInCallback) { _throwInCallback = throwInCallback; } public bool WasInvoked { get; private set; } public IAsyncResult AsyncResult { get; private set; } public Exception CallbackException { get; private set; } public void Callback(IAsyncResult result) { WasInvoked = true; AsyncResult = result; if (_throwInCallback) { CallbackException = new Exception("Callback exception"); throw CallbackException; } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/ByteRangeStreamTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http.Internal { public class ByteRangeStreamTest { // from, to, expectedText public static TheoryDataSet CopyBoundsData { get { return new TheoryDataSet { { null, 23, "This is the whole text." }, { 0, null, "This is the whole text." }, { 0, 22, "This is the whole text." }, { 0, 3, "This" }, { 12, 16, "whole" }, { null, 5, "text." }, { 18, null, "text." }, { 18, 22, "text." }, }; } } // from, to, innerLength, effectiveLength public static TheoryDataSet ReadBoundsData { get { return new TheoryDataSet { { 0, 9, 20, 10 }, { 8, 8, 10, 1 }, { 0, 19, 20, 20 }, { 0, 29, 40, 30 }, { 0, 29, 20, 20 }, { 19, 29, 20, 1 }, }; } } // from, to, innerLength, effectiveLength for reads limited by byte[] size. public static TheoryDataSet ReadBoundsDataWithLimit { get { return new TheoryDataSet { { 0, 9, 20, 10 }, { 8, 8, 10, 1 }, { 0, 19, 20, 20 }, { 0, 29, 40, 25 }, { 0, 29, 20, 20 }, { 19, 29, 20, 1 }, }; } } [Fact] public void Ctor_ThrowsOnNullInnerStream() { var range = new RangeItemHeaderValue(0, 10); Assert.ThrowsArgumentNull(() => new ByteRangeStream(innerStream: null, range: range), "innerStream"); } [Fact] public void Ctor_ThrowsOnNullRange() { Assert.ThrowsArgumentNull(() => new ByteRangeStream(innerStream: Stream.Null, range: null), "range"); } [Fact] public void Ctor_ThrowsIfCantSeekInnerStream() { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(false); var range = new RangeItemHeaderValue(0, 10); // Act/Assert Assert.ThrowsArgument(() => new ByteRangeStream(mockInnerStream.Object, range), "innerStream"); } [Fact] public void Ctor_ThrowsIfLowerRangeExceedsInnerStream() { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(5); var range = new RangeItemHeaderValue(10, 20); // Act/Assert Assert.ThrowsArgumentOutOfRange(() => new ByteRangeStream(mockInnerStream.Object, range), "range", "The 'From' value of the range must be less than or equal to 5.", false, 10); } [Fact] public void Ctor_SetsContentRange() { // Arrange var expectedContentRange = new ContentRangeHeaderValue(5, 9, 20); var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(20); var range = new RangeItemHeaderValue(5, 9); // Act using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Assert Assert.Equal(expectedContentRange, rangeStream.ContentRange); } } [Theory] [InlineData(0)] [InlineData(-1)] public void Ctor_ThrowsIfInnerStreamLengthIsLessThanOne(int innerLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(null, 0); // Act/Assert Assert.ThrowsArgumentOutOfRange( () => new ByteRangeStream(mockInnerStream.Object, range), "innerStream", "The stream over which 'ByteRangeStream' provides a range view must have a length greater than or " + "equal to 1.", false, innerLength); } [Theory] [PropertyData("ReadBoundsData")] public void Ctor_SetsLength(int from, int to, int innerLength, int expectedLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(from, to); // Act using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Assert Assert.Equal(expectedLength, rangeStream.Length); } } [Theory] [PropertyData("CopyBoundsData")] public async Task CopyTo_ReadsSpecifiedRange(long? from, long? to, string expectedText) { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(from, to); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) using (var targetStream = new MemoryStream()) using (var reader = new StreamReader(targetStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); // Act using (var rangeStream = new ByteRangeStream(innerStream, range)) { rangeStream.CopyTo(targetStream); } // Assert targetStream.Position = 0L; var text = await reader.ReadToEndAsync(); Assert.Equal(expectedText, text); } } [Theory] [PropertyData("CopyBoundsData")] public async Task CopyToAsync_ReadsSpecifiedRange(long? from, long? to, string expectedText) { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(from, to); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) using (var targetStream = new MemoryStream()) using (var reader = new StreamReader(targetStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); // Act using (var rangeStream = new ByteRangeStream(innerStream, range)) { await rangeStream.CopyToAsync(targetStream); } // Assert targetStream.Position = 0L; var text = await reader.ReadToEndAsync(); Assert.Equal(expectedText, text); } } [Fact] public void Position_ThrowsOnNegativeValue() { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(10L); var range = new RangeItemHeaderValue(0, 25L); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act & Assert Assert.Throws(() => rangeStream.Position = -1L); } } [Theory] [InlineData(null)] [InlineData(0L)] [InlineData(7L)] [InlineData(9L)] public void Position_ReturnsZeroInitially(long? from) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(10L); var range = new RangeItemHeaderValue(from, 25L); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act var position = rangeStream.Position; // Assert Assert.Equal(0L, position); } } [Fact] public void Position_CanBeSetAfterLength() { // Arrange var expectedPosition = 300L; var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(10L); var range = new RangeItemHeaderValue(0L, 10L); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act rangeStream.Position = expectedPosition; // Assert Assert.Equal(expectedPosition, rangeStream.Position); } } [Fact] public async Task Position_PositionsNextRead() { // Arrange var originalText = "890123456789"; var range = new RangeItemHeaderValue(2L, null); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { // Act rangeStream.Position = 5L; // Assert var read = rangeStream.ReadByte(); Assert.Equal('5', (char)read); } } } #if !Testing_NetStandard1_3 // BeginX and EndX are not supported on Streams in netstandard1.3 [Theory] [PropertyData("ReadBoundsDataWithLimit")] public void BeginRead_ReadsEffectiveLengthBytes(int from, int to, int innerLength, int effectiveLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(from, to); var data = new byte[25]; var offset = 5; var callback = new AsyncCallback(_ => { }); var userState = new object(); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act var result = rangeStream.BeginRead(data, offset, data.Length, callback, userState); rangeStream.EndRead(result); // Assert mockInnerStream.Verify( s => s.BeginRead(data, offset, effectiveLength, callback, userState), Times.Once()); Assert.Equal(effectiveLength, rangeStream.Position); } } #endif [Fact] public async Task BeginRead_CanReadAfterLength() { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(0L, null); var data = new byte[25]; var callback = new AsyncCallback(_ => { }); var userState = new object(); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { rangeStream.Position = 50L; // Act var result = rangeStream.BeginRead(data, 0, data.Length, callback, userState); var read = rangeStream.EndRead(result); // Assert Assert.Equal(0, read); } } } [Theory] [PropertyData("ReadBoundsDataWithLimit")] public void Read_ReadsEffectiveLengthBytes(int from, int to, int innerLength, int effectiveLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(from, to); var data = new byte[25]; var offset = 5; using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act rangeStream.Read(data, offset, data.Length); // Assert mockInnerStream.Verify(s => s.Read(data, offset, effectiveLength), Times.Once()); Assert.Equal(effectiveLength, rangeStream.Position); } } [Fact] public async Task Read_CanReadAfterLength() { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(0L, null); var data = new byte[25]; using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { rangeStream.Position = 50L; // Act var read = rangeStream.Read(data, 0, data.Length); // Assert Assert.Equal(0, read); } } } [Theory] [PropertyData("ReadBoundsDataWithLimit")] public async Task ReadAsync_ReadsEffectiveLengthBytes(int from, int to, int innerLength, int effectiveLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(from, to); var data = new byte[25]; var offset = 5; using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act await rangeStream.ReadAsync(data, offset, data.Length); // Assert mockInnerStream.Verify( s => s.ReadAsync(data, offset, effectiveLength, CancellationToken.None), Times.Once()); Assert.Equal(effectiveLength, rangeStream.Position); } } [Fact] public async Task ReadAsync_CanReadAfterLength() { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(0L, null); var data = new byte[25]; using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { rangeStream.Position = 50L; // Act var read = await rangeStream.ReadAsync(data, 0, data.Length); // Assert Assert.Equal(0, read); } } } [Theory] [PropertyData("ReadBoundsData")] public void ReadByte_ReadsEffectiveLengthTimes(int from, int to, int innerLength, int effectiveLength) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(innerLength); var range = new RangeItemHeaderValue(from, to); var counter = 0; using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act while (rangeStream.ReadByte() != -1) { counter++; } // Assert mockInnerStream.Verify(s => s.ReadByte(), Times.Exactly(effectiveLength)); Assert.Equal(effectiveLength, counter); Assert.Equal(effectiveLength, rangeStream.Position); } } [Fact] public async Task ReadByte_CanReadAfterLength() { // Arrange var originalText = "This is the whole text."; var range = new RangeItemHeaderValue(0L, null); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { rangeStream.Position = 50L; // Act var read = rangeStream.ReadByte(); // Assert Assert.Equal(-1, read); } } } [Theory] [InlineData(-1, SeekOrigin.Begin)] [InlineData(-1, SeekOrigin.Current)] [InlineData(-11, SeekOrigin.End)] public void Seek_ThrowsIfBeforeOrigin(int offset, SeekOrigin origin) { // Arrange var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(10L); var range = new RangeItemHeaderValue(0, 25L); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act & Assert Assert.Throws(() => rangeStream.Seek(offset, origin)); } } [Theory] [InlineData(25, SeekOrigin.Begin)] [InlineData(25, SeekOrigin.Current)] [InlineData(15, SeekOrigin.End)] public void Seek_CanMoveAfterLength(int offset, SeekOrigin origin) { // Arrange var expectedPosition = 25L; var mockInnerStream = new Mock(); mockInnerStream.Setup(s => s.CanSeek).Returns(true); mockInnerStream.Setup(s => s.Length).Returns(10L); var range = new RangeItemHeaderValue(0L, 10L); using (var rangeStream = new ByteRangeStream(mockInnerStream.Object, range)) { // Act var newPosition = rangeStream.Seek(offset, origin); // Assert Assert.Equal(expectedPosition, newPosition); Assert.Equal(expectedPosition, rangeStream.Position); } } [Theory] [InlineData(5, SeekOrigin.Begin)] [InlineData(5, SeekOrigin.Current)] [InlineData(-5, SeekOrigin.End)] public async Task Seek_PositionsNextRead(int offset, SeekOrigin origin) { // Arrange var originalText = "890123456789"; var range = new RangeItemHeaderValue(2L, null); using (var innerStream = new MemoryStream()) using (var writer = new StreamWriter(innerStream)) { await writer.WriteAsync(originalText); await writer.FlushAsync(); using (var rangeStream = new ByteRangeStream(innerStream, range)) { // Act rangeStream.Seek(offset, origin); // Assert var read = rangeStream.ReadByte(); Assert.Equal('5', (char)read); } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/ConcurrentDictionaryTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Microsoft.TestCommon; namespace System.Collections.Concurrent { public class ConcurrentDictionaryTests { [Fact] public void ContainsKey_ReturnsFalseWhenKeyIsNotPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); // Act & Assert Assert.False(dictionary.ContainsKey(3)); } [Fact] public void ContainsKey_ReturnsTrueWhenKeyIsPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); // Act dictionary.TryAdd(1, 2); // Assert Assert.True(dictionary.ContainsKey(1)); } [Fact] public void GetOrAdd_AddsNewValueWhenKeyIsNotPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); // Act int returnedValue = dictionary.GetOrAdd(1, (key) => { return ++key; }); // Assert Assert.Equal(2, returnedValue); } [Fact] public void GetOrAdd_ReturnsExistingValueWhenKeyIsPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); dictionary.TryAdd(1, -1); // Act int returnedValue = dictionary.GetOrAdd(1, (key) => { return ++key; }); // Assert Assert.Equal(-1, returnedValue); } [Fact] public void TryAdd_ReturnsTrueWhenKeyIsNotPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); // Act bool result = dictionary.TryAdd(1, 2); // Assert Assert.True(result); Assert.True(dictionary.ContainsKey(1)); } [Fact] public void TryAdd_ReturnsFalseWhenKeyIsPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); dictionary.TryAdd(1, 2); // Act bool result = dictionary.TryAdd(1, 2); // Assert Assert.False(result); } [Fact] public void AddOrUpdate_AddsValueWhenKeyIsNotPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); // Act int result = dictionary.AddOrUpdate(1, 2, (key, current) => { return ++current; }); // Assert Assert.Equal(2, result); Assert.Equal(2, dictionary.GetOrAdd(1, (key) => { return -1; })); } [Fact] public void AddOrUpdate_UpdatesValueWhenKeyIsPresent() { // Arrange ConcurrentDictionary dictionary = new ConcurrentDictionary(); dictionary.TryAdd(1, 2); // Act int result = dictionary.AddOrUpdate(1, 2, (key, current) => { return ++current; }); // Assert Assert.Equal(3, result); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/DelegatingStreamTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Mocks; using System.Threading; using Microsoft.TestCommon; using Moq; using Moq.Protected; namespace System.Net.Http.Internal { public class DelegatingStreamTest { [Fact] public void DelegatingStream_CtorThrowsOnNull() { Assert.ThrowsArgumentNull(() => new MockDelegatingStream(null), "innerStream"); } [Fact] public void DelegatingStream_CanRead() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act bool canRead = mockStream.CanRead; // Assert mockInnerStream.Verify(s => s.CanRead, Times.Once()); } [Fact] public void DelegatingStream_CanSeek() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act bool canSeek = mockStream.CanSeek; // Assert mockInnerStream.Verify(s => s.CanSeek, Times.Once()); } [Fact] public void DelegatingStream_CanWrite() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act bool canWrite = mockStream.CanWrite; // Assert mockInnerStream.Verify(s => s.CanWrite, Times.Once()); } [Fact] public void DelegatingStream_Length() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act long length = mockStream.Length; // Assert mockInnerStream.Verify(s => s.Length, Times.Once()); } [Fact] public void DelegatingStream_Position() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act long position = mockStream.Position; // Assert mockInnerStream.Verify(s => s.Position, Times.Once()); } [Fact] public void DelegatingStream_ReadTimeout() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act int readTimeout = mockStream.ReadTimeout; // Assert mockInnerStream.Verify(s => s.ReadTimeout, Times.Once()); } [Fact] public void DelegatingStream_CanTimeout() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act bool canTimeout = mockStream.CanTimeout; // Assert mockInnerStream.Verify(s => s.CanTimeout, Times.Once()); } [Fact] public void DelegatingStream_WriteTimeout() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act int writeTimeout = mockStream.WriteTimeout; // Assert mockInnerStream.Verify(s => s.WriteTimeout, Times.Once()); } [Fact] public void DelegatingStream_Dispose() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act mockStream.Dispose(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Once(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Once()); } [Fact] public void DelegatingStream_Close() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act mockStream.Close(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Once(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Once()); } [Fact] public void DelegatingStream_Seek() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); long offset = 1; SeekOrigin origin = SeekOrigin.End; // Act long seek = mockStream.Seek(offset, origin); // Assert mockInnerStream.Verify(s => s.Seek(offset, origin), Times.Once()); } [Fact] public void DelegatingStream_Read() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; // Act mockStream.Read(buffer, offset, count); // Assert mockInnerStream.Verify(s => s.Read(buffer, offset, count), Times.Once()); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 [Fact] public void DelegatingStream_BeginRead() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; AsyncCallback callback = new AsyncCallback((asyncResult) => { }); object state = new object(); // Act mockStream.BeginRead(buffer, offset, count, callback, state); // Assert mockInnerStream.Verify(s => s.BeginRead(buffer, offset, count, callback, state), Times.Once()); } [Fact] public void DelegatingStream_EndRead() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); Mock mockIAsyncResult = new Mock(); // Act int endRead = mockStream.EndRead(mockIAsyncResult.Object); // Assert mockInnerStream.Verify(s => s.EndRead(mockIAsyncResult.Object), Times.Once()); } #endif [Fact] public void DelegatingStream_ReadAsyc() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; // Act mockStream.ReadAsync(buffer, offset, count, CancellationToken.None); // Assert mockInnerStream.Verify(s => s.ReadAsync(buffer, offset, count, CancellationToken.None), Times.Once()); } [Fact] public void DelegatingStream_ReadByte() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act int readByte = mockStream.ReadByte(); // Assert mockInnerStream.Verify(s => s.ReadByte(), Times.Once()); } [Fact] public void DelegatingStream_Flush() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act mockStream.Flush(); // Assert mockInnerStream.Verify(s => s.Flush(), Times.Once()); } [Fact] public void DelegatingStream_SetLength() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); // Act mockStream.SetLength(10L); // Assert mockInnerStream.Verify(s => s.SetLength(10L), Times.Once()); } [Fact] public void DelegatingStream_Write() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; // Act mockStream.Write(buffer, offset, count); // Assert mockInnerStream.Verify(s => s.Write(buffer, offset, count), Times.Once()); } #if !Testing_NetStandard1_3 // BeginX and EndX not supported on Streams in netstandard1.3 [Fact] public void DelegatingStream_BeginWrite() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; AsyncCallback callback = new AsyncCallback((asyncResult) => { }); object state = new object(); // Act mockStream.BeginWrite(buffer, offset, count, callback, state); // Assert mockInnerStream.Verify(s => s.BeginWrite(buffer, offset, count, callback, state), Times.Once()); } [Fact] public void DelegatingStream_EndWrite() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); Mock mockIAsyncResult = new Mock(); // Act mockStream.EndWrite(mockIAsyncResult.Object); // Assert mockInnerStream.Verify(s => s.EndWrite(mockIAsyncResult.Object), Times.Once()); } #endif [Fact] public void DelegatingStream_WriteAsync() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte[] buffer = new byte[2]; int offset = 1; int count = 1; // Act mockStream.WriteAsync(buffer, offset, count, CancellationToken.None); // Assert mockInnerStream.Verify(s => s.WriteAsync(buffer, offset, count, CancellationToken.None), Times.Once()); } [Fact] public void DelegatingStream_WriteByte() { // Arrange Mock mockInnerStream = new Mock(); MockDelegatingStream mockStream = new MockDelegatingStream(mockInnerStream.Object); byte data = new byte(); // Act mockStream.WriteByte(data); // Assert mockInnerStream.Verify(s => s.WriteByte(data), Times.Once()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/HttpValueCollectionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting; using System.Net.Http.Formatting.Internal; using System.Web.WebPages.TestUtils; using Microsoft.TestCommon; namespace System.Net.Http.Internal { public class HttpValueCollectionTest { #if !NETCOREAPP // Unused on .NET Core. private static readonly int _maxCollectionKeys = 1000; #endif private static HttpValueCollection CreateInstance() { return HttpValueCollection.Create(); } #if !NETCOREAPP // Unsupported on .NET Core. private static void RunInIsolation(Action action) { AppDomainUtils.RunInSeparateAppDomain(action); } #endif public static TheoryDataSet>> KeyValuePairs { get { return new TheoryDataSet>>() { new List> { new KeyValuePair(null, null), new KeyValuePair("n0", ""), new KeyValuePair("n1", "v1"), new KeyValuePair("n@2", "v@2"), new KeyValuePair("n 3", "v 3"), new KeyValuePair("n+4", "v+4"), new KeyValuePair("n;5", "v;5"), new KeyValuePair("n=5", "v=5"), } }; } } internal class TestPropertyHolder { public static TheoryDataSet ToStringTestData { get { TheoryDataSet dataSet = new TheoryDataSet(); var hvc1 = CreateInstance(); hvc1.Add(null, null); dataSet.Add(hvc1, ""); var hvc2 = CreateInstance(); hvc2.Add("name", null); dataSet.Add(hvc2, "name"); var hvc3 = CreateInstance(); hvc3.Add("name", ""); dataSet.Add(hvc3, "name"); var hvc4 = CreateInstance(); hvc4.Add("na me", ""); dataSet.Add(hvc4, "na+me"); string encoded5 = "n%22%2C%3B%5Cn"; var hvc5 = CreateInstance(); hvc5.Add("n\",;\\n", ""); dataSet.Add(hvc5, encoded5); var hvc6 = CreateInstance(); hvc6.Add("", "v1"); hvc6.Add("", "v2"); hvc6.Add("", "v3"); hvc6.Add("", "v4"); dataSet.Add(hvc6, "=v1&=v2&=v3&=v4"); var hvc7 = CreateInstance(); hvc7.Add("n1", "v1"); hvc7.Add("n2", "v2"); hvc7.Add("n3", "v3"); hvc7.Add("n4", "v4"); dataSet.Add(hvc7, "n1=v1&n2=v2&n3=v3&n4=v4"); string encoded8 = "n%2C1=v%2C1&n%3B2=v%3B2"; var hvc8 = CreateInstance(); hvc8.Add("n,1", "v,1"); hvc8.Add("n;2", "v;2"); dataSet.Add(hvc8, encoded8); string encoded9 = "n1=%26&n2=%3B&n3=%26&n4=%2B&n5=%26&n6=%3D&n7=%26"; var hvc9 = CreateInstance(); hvc9.Add("n1", "&"); hvc9.Add("n2", ";"); hvc9.Add("n3", "&"); hvc9.Add("n4", "+"); hvc9.Add("n5", "&"); hvc9.Add("n6", "="); hvc9.Add("n7", "&"); dataSet.Add(hvc9, encoded9); var hvc10 = CreateInstance(); hvc10.Add("n1", "&"); hvc10.Add("n2", null); hvc10.Add("n3", "null"); dataSet.Add(hvc10, "n1=%26&n2&n3=null"); return dataSet; } } } [Fact] public void Create_CreatesEmptyCollection() { var nvc = CreateInstance(); Assert.IsType(nvc); Assert.Empty(nvc); } #if !NETCOREAPP // Able to run on a separate AppDomain only on .NET Framework. // This set of tests requires running on a separate appdomain so we don't // touch the static property MediaTypeFormatter.MaxHttpCollectionKeys. [Fact] public void Create_CreateTooManyKeysThrows() { RunInIsolation(Create_CreateTooManyKeysThrowsPrivate); } private static void Create_CreateTooManyKeysThrowsPrivate() { // Arrange MediaTypeFormatter.MaxHttpCollectionKeys = _maxCollectionKeys; List> list = new List>(); for (int i = 0; i < _maxCollectionKeys + 1; i++) { list.Add(new KeyValuePair(i.ToString(), i.ToString())); } // Act & Assert Assert.Throws(() => HttpValueCollection.Create(list), TooManyKeysError); } private static string TooManyKeysError { get { return "The number of keys in a NameValueCollection has exceeded the limit of '" + _maxCollectionKeys + "'. You can adjust it by modifying the MaxHttpCollectionKeys property on the 'System.Net.Http.Formatting.MediaTypeFormatter' class."; } } [Fact] public void Create_CreateDoesntThrowTooManyValues() { RunInIsolation(Create_CreateDoesntThrowTooManyValuesPrivate); } private static void Create_CreateDoesntThrowTooManyValuesPrivate() { // note this is static, but also the expected type in a real run. MediaTypeFormatter.MaxHttpCollectionKeys = _maxCollectionKeys; List> list = new List>(); for (int i = 0; i < _maxCollectionKeys + 1; i++) { list.Add(new KeyValuePair("key", i.ToString())); } Assert.DoesNotThrow(() => HttpValueCollection.Create(list)); } [Fact] public void AddTooManyKeysThrows() { RunInIsolation(AddTooManyKeysThrowsPrivate); } private static void AddTooManyKeysThrowsPrivate() { // Note this is static, but also the expected type in a real run. MediaTypeFormatter.MaxHttpCollectionKeys = _maxCollectionKeys; HttpValueCollection collection = CreateInstance(); for (int i = 0; i < _maxCollectionKeys; i++) { collection.Add(i.ToString(), i.ToString()); } // Act && Assert Assert.Throws( () => collection.Add(_maxCollectionKeys.ToString(), _maxCollectionKeys.ToString()), TooManyKeysError); } [Fact] public void AddDoesntThrowTooManyValues() { RunInIsolation(AddDoesntThrowTooManyValuesPrivate); } private static void AddDoesntThrowTooManyValuesPrivate() { // Note this is static, but also the expected type in a real run. MediaTypeFormatter.MaxHttpCollectionKeys = _maxCollectionKeys; HttpValueCollection collection = CreateInstance(); // Act && Assert Assert.DoesNotThrow(() => { for (int i = 0; i < 1001; i++) { collection.Add("key", i.ToString()); } }); } #endif [Theory] [PropertyData("KeyValuePairs")] public void Create_InitializesCorrectly(IEnumerable> input) { var nvc = HttpValueCollection.Create(input); int count = input.Count(); Assert.IsType(nvc); Assert.Equal(count, nvc.Count); int index = 0; foreach (KeyValuePair kvp in input) { string expectedKey = kvp.Key ?? String.Empty; string expectedValue = kvp.Value ?? String.Empty; string actualKey = nvc.AllKeys[index]; string actualValue = nvc[index]; index++; Assert.Equal(expectedKey, actualKey); Assert.Equal(expectedValue, actualValue); } } [Theory] [PropertyData("KeyValuePairs")] public void GetIsEquivalentToIndexerProperty(IEnumerable> input) { var nvc = HttpValueCollection.Create(input); int count = input.Count(); Assert.IsType(nvc); Assert.Equal(count, nvc.Count); foreach (KeyValuePair kvp in input) { Assert.Equal(nvc[kvp.Key], nvc.Get(kvp.Key)); } } [Theory] [PropertyData("ToStringTestData", PropertyType = typeof(TestPropertyHolder))] internal void ToString_GeneratesCorrectOutput(HttpValueCollection input, string expectedOutput) { string actualOutput = input.ToString(); Assert.Equal(expectedOutput, actualOutput); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/NonClosingDelegatingStreamTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Mocks; using Microsoft.TestCommon; using Moq; using Moq.Protected; namespace System.Net.Http.Internal { public class NonClosingDelegatingStreamTest { [Fact] public void NonClosingDelegatingStream_Dispose() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; MockNonClosingDelegatingStream mockStream = new MockNonClosingDelegatingStream(mockInnerStream.Object); // Act mockStream.Dispose(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Never(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Never()); } [Fact] public void NonClosingDelegatingStream_Close() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; MockNonClosingDelegatingStream mockStream = new MockNonClosingDelegatingStream(mockInnerStream.Object); // Act mockStream.Close(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Never(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Never()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Internal/TranscodingStreamTests.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // From https://github.com/dotnet/runtime/blob/88868b7a781f4e5b9037b8721f30440207a7aa42/src/libraries/System.Text.Encoding/tests/Encoding/TranscodingStreamTests.cs using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; #nullable enable annotations namespace System.Text.Tests { public class TranscodingStreamTests { public static IEnumerable ReadWriteTestBufferLengths { get { yield return new object[] { 1 }; yield return new object[] { 4 * 1024 }; yield return new object[] { 128 * 1024 }; yield return new object[] { 2 * 1024 * 1024 }; } } #if Testing_NetStandard1_3 || Testing_NetStandard2_0 // .NET Framework implementation loses track of cancellation token. [Fact] public void AsyncMethods_ReturnCanceledTaskIfCancellationTokenTripped() { // Arrange CancellationTokenSource cts = new(); CancellationToken expectedCancellationToken = cts.Token; cts.Cancel(); var innerStreamMock = new Mock(MockBehavior.Strict); // only CanRead/CanWrite should ever be invoked innerStreamMock.Setup(o => o.CanRead).Returns(true); innerStreamMock.Setup(o => o.CanWrite).Returns(true); Stream transcodingStream = new TranscodingStream(innerStreamMock.Object, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); // Act & assert RunTest(() => transcodingStream.ReadAsync(new byte[0], 0, 0, expectedCancellationToken)); RunTest(() => transcodingStream.WriteAsync(new byte[0], 0, 0, expectedCancellationToken)); #if NETCOREAPP || NETSTANDARD2_1 RunTest(() => transcodingStream.ReadAsync(Memory.Empty, expectedCancellationToken).AsTask()); RunTest(() => transcodingStream.WriteAsync(ReadOnlyMemory.Empty, expectedCancellationToken).AsTask()); #endif void RunTest(Func callback) { Task task = callback(); Assert.True(task.IsCanceled); Assert.Equal(expectedCancellationToken, Assert.Throws(() => task.GetAwaiter().GetResult()).CancellationToken); } } #endif [Fact] public void CreateTranscodingStream_InvalidArgs() { Assert.ThrowsArgumentNull(() => new TranscodingStream(null, Encoding.UTF8, Encoding.UTF8), "innerStream"); Assert.ThrowsArgumentNull(() => new TranscodingStream(Stream.Null, null, Encoding.UTF8), "innerEncoding"); Assert.ThrowsArgumentNull(() => new TranscodingStream(Stream.Null, Encoding.UTF8, null), "thisEncoding"); } [Theory] [InlineData(true)] [InlineData(false)] public void CanRead_DelegatesToInnerStream(bool expectedCanRead) { // Arrange var innerStreamMock = new Mock(); innerStreamMock.Setup(o => o.CanRead).Returns(expectedCanRead); Stream transcodingStream = new TranscodingStream(innerStreamMock.Object, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); // Act bool actualCanReadBeforeDispose = transcodingStream.CanRead; transcodingStream.Dispose(); bool actualCanReadAfterDispose = transcodingStream.CanRead; // Assert Assert.Equal(expectedCanRead, actualCanReadBeforeDispose); Assert.False(actualCanReadAfterDispose); } [Theory] [InlineData(true)] [InlineData(false)] public void CanWrite_DelegatesToInnerStream(bool expectedCanWrite) { // Arrange var innerStreamMock = new Mock(); innerStreamMock.Setup(o => o.CanWrite).Returns(expectedCanWrite); Stream transcodingStream = new TranscodingStream(innerStreamMock.Object, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); // Act bool actualCanWriteBeforeDispose = transcodingStream.CanWrite; transcodingStream.Dispose(); bool actualCanWriteAfterDispose = transcodingStream.CanWrite; // Assert Assert.Equal(expectedCanWrite, actualCanWriteBeforeDispose); Assert.False(actualCanWriteAfterDispose); } [Fact] public void Dispose_MakesMostSubsequentOperationsThrow() { // Arrange MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); // Act transcodingStream.Dispose(); // Assert // For Task/ValueTask-returning methods, we want the exception to be thrown synchronously. Assert.False(transcodingStream.CanRead); Assert.False(transcodingStream.CanSeek); Assert.False(transcodingStream.CanWrite); #if true // Not overriding these and base Stream's BeginXYZ methods check CanXYZ first, throwing NotSupportedException. Assert.Throws(() => transcodingStream.BeginRead(new byte[0], 0, 0, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[0], 0, 0, null, null)); #else Assert.Throws(() => transcodingStream.BeginRead(new byte[0], 0, 0, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[0], 0, 0, null, null)); #endif #if NETCOREAPP || NETSTANDARD2_1 Assert.Throws(() => transcodingStream.Read(Span.Empty)); Assert.Throws(() => (object)transcodingStream.ReadAsync(Memory.Empty)); Assert.Throws(() => transcodingStream.Write(ReadOnlySpan.Empty)); Assert.Throws(() => (object)transcodingStream.WriteAsync(ReadOnlyMemory.Empty)); #endif Assert.Throws(() => transcodingStream.Flush()); Assert.Throws(() => (object)transcodingStream.FlushAsync()); Assert.Throws(() => transcodingStream.Read(new byte[0], 0, 0)); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[0], 0, 0)); Assert.Throws(() => transcodingStream.ReadByte()); Assert.Throws(() => transcodingStream.Write(new byte[0], 0, 0)); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[0], 0, 0)); Assert.Throws(() => transcodingStream.WriteByte((byte)'x')); } [Fact] public void Dispose_WithLeaveOpenFalse_DisposesInnerStream() { // Sync MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8, leaveOpen: false); transcodingStream.Dispose(); transcodingStream.Dispose(); // calling it a second time should no-op Assert.Throws(() => innerStream.Read(Array.Empty(), 0, 0)); // Async #if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1 innerStream = new MemoryStream(); transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8, leaveOpen: false); transcodingStream.DisposeAsync().GetAwaiter().GetResult(); transcodingStream.DisposeAsync().GetAwaiter().GetResult(); // calling it a second time should no-op Assert.Throws(() => innerStream.Read(Span.Empty)); #endif } [Fact] public void Dispose_WithLeaveOpenTrue_DoesNotDisposeInnerStream() { // Sync MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); transcodingStream.Dispose(); transcodingStream.Dispose(); // calling it a second time should no-op innerStream.Read(Array.Empty(), 0, 0); // shouldn't throw // Async #if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1 innerStream = new MemoryStream(); transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); transcodingStream.DisposeAsync().GetAwaiter().GetResult(); transcodingStream.DisposeAsync().GetAwaiter().GetResult(); // calling it a second time should no-op innerStream.Read(Span.Empty); // shouldn't throw #endif } // Moq heavily utilizes RefEmit, which does not work on most aot workloads [Fact] public void Flush_FlushesInnerStreamButNotDecodedState() { // Arrange CancellationToken expectedCancellationToken = new CancellationTokenSource().Token; Task expectedFlushAsyncTask = Task.FromResult("just some task"); var innerStreamMock = new Mock() { CallBase = true }; innerStreamMock.Setup(o => o.FlushAsync(expectedCancellationToken)).Returns(expectedFlushAsyncTask); Stream transcodingStream = new TranscodingStream(innerStreamMock.Object, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); transcodingStream.Write(new byte[] { 0x7A, 0xE0 }, 0, 2); innerStreamMock.Verify(o => o.Flush(), Times.Never); innerStreamMock.Verify(o => o.FlushAsync(It.IsAny()), Times.Never); // Act & assert - sync flush transcodingStream.Flush(); innerStreamMock.Verify(o => o.Flush(), Times.Once); innerStreamMock.Verify(o => o.FlushAsync(It.IsAny()), Times.Never); // Act & assert - async flush // This also validates that we flowed the CancellationToken as expected Task actualFlushAsyncReturnedTask = transcodingStream.FlushAsync(expectedCancellationToken); Assert.Same(expectedFlushAsyncTask, actualFlushAsyncReturnedTask); innerStreamMock.Verify(o => o.Flush(), Times.Once); innerStreamMock.Verify(o => o.FlushAsync(expectedCancellationToken), Times.Once); Assert.Equal("z", Encoding.UTF8.GetString(innerStreamMock.Object.ToArray())); // [ E0 ] shouldn't have been flushed } [Fact] public void IdenticalInnerAndOuterEncodings_DoesNotActAsPassthrough() { // Test read // [ C0 ] is never a valid UTF-8 byte, should be replaced with U+FFFD MemoryStream innerStream = new(new byte[] { 0xC0 }); Stream transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8); Assert.Equal(0xEF, transcodingStream.ReadByte()); Assert.Equal(0xBF, transcodingStream.ReadByte()); Assert.Equal(0xBD, transcodingStream.ReadByte()); Assert.Equal(-1 /* eof */, transcodingStream.ReadByte()); // Test write innerStream = new MemoryStream(); transcodingStream = new TranscodingStream(innerStream, Encoding.UTF8, Encoding.UTF8); transcodingStream.WriteByte(0xC0); Assert.Equal(new byte[] { 0xEF, 0xBF, 0xBD }, innerStream.ToArray()); } [Theory] [PropertyData(nameof(ReadWriteTestBufferLengths))] public void Read_ByteArray(int bufferLength) { // Tests TranscodingStream.Read(byte[], int, int) byte[] buffer = new byte[bufferLength + 3]; RunReadTest((transcodingStream, sink) => { int numBytesRead = transcodingStream.Read(buffer, 1, bufferLength); Assert.True(numBytesRead >= 0); Assert.True(numBytesRead <= bufferLength); sink.Write(buffer, 1, numBytesRead); return numBytesRead; }); } [Fact] public void Read_ByteArray_WithInvalidArgs_Throws() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); Assert.ThrowsArgumentNull(() => transcodingStream.Read(null, 0, 0), "buffer"); Assert.Throws(() => transcodingStream.Read(new byte[5], -1, -1)); Assert.Throws(() => transcodingStream.Read(new byte[5], 3, -1)); Assert.Throws(() => transcodingStream.Read(new byte[5], 5, 1)); Assert.Throws(() => transcodingStream.Read(new byte[5], 6, -1)); Assert.Throws(() => transcodingStream.Read(new byte[5], 6, 0)); } [Fact] public void Read_ByteByByte() { // Tests TranscodingStream.ReadByte RunReadTest((transcodingStream, sink) => { int value = transcodingStream.ReadByte(); if (value < 0) { return 0; } sink.WriteByte(checked((byte)value)); return 1; }); } #if NETCOREAPP || NETSTANDARD2_1 [Theory] [PropertyData(nameof(ReadWriteTestBufferLengths))] public void Read_Span(int bufferLength) { // Tests TranscodingStream.Read(Span) byte[] buffer = new byte[bufferLength]; RunReadTest((transcodingStream, sink) => { int numBytesRead = transcodingStream.Read(buffer.AsSpan()); Assert.True(numBytesRead >= 0); Assert.True(numBytesRead <= bufferLength); sink.Write(buffer.AsSpan(0, numBytesRead)); return numBytesRead; }); } #endif private void RunReadTest(Func callback) { MemoryStream sink = new(); MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream(innerStream, innerEncoding: Encoding.UTF8, thisEncoding: CustomAsciiEncoding); // Test with a small string, then test with a large string RunOneTestIteration(128); RunOneTestIteration(10 * 1024 * 1024); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF // Now put some invalid data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC0); // [ C0 ] is never valid in UTF-8 innerStream.Position = 0; sink.SetLength(0); // reset int numBytesReadJustNow; do { numBytesReadJustNow = callback(transcodingStream, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF // Now put some incomplete data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC2); // [ C2 ] must be followed by [ 80..BF ] in UTF-8 innerStream.Position = 0; sink.SetLength(0); // reset do { numBytesReadJustNow = callback(transcodingStream, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF void RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset string expectedStringContents = GetVeryLongAsciiString(stringLength); innerStream.SetLength(0); // reset var bytes = Encoding.UTF8.GetBytes(expectedStringContents); innerStream.Write(bytes, 0, bytes.Length); innerStream.Position = 0; int numBytesReadJustNow; do { numBytesReadJustNow = callback(transcodingStream, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal(expectedStringContents, ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); } } [Fact] public Task ReadApm() { // Tests TranscodingStream.BeginRead / EndRead byte[] buffer = new byte[1024 * 1024]; return RunReadTestAsync((transcodingStream, cancellationToken, sink) => { TaskCompletionSource tcs = new(); object expectedState = new(); try { IAsyncResult asyncResult = transcodingStream.BeginRead(buffer, 1, buffer.Length - 2, (asyncResult) => { try { int numBytesReadJustNow = transcodingStream.EndRead(asyncResult); Assert.True(numBytesReadJustNow >= 0); Assert.True(numBytesReadJustNow < buffer.Length - 3); sink.Write(buffer, 1, numBytesReadJustNow); tcs.SetResult(numBytesReadJustNow); } catch (Exception ex) { tcs.SetException(ex); } }, expectedState); Assert.Same(expectedState, asyncResult.AsyncState); } catch (Exception ex) { tcs.SetException(ex); } return new ValueTask(tcs.Task); }, suppressExpectedCancellationTokenAsserts: true); // APM pattern doesn't allow flowing CancellationToken } [Theory] [PropertyData(nameof(ReadWriteTestBufferLengths))] public Task ReadAsync_ByteArray(int bufferLength) { // Tests TranscodingStream.ReadAsync(byte[], int, int, CancellationToken) byte[] buffer = new byte[bufferLength + 3]; return RunReadTestAsync(async (transcodingStream, cancellationToken, sink) => { int numBytesRead = await transcodingStream.ReadAsync(buffer, 1, bufferLength, cancellationToken); Assert.True(numBytesRead >= 0); Assert.True(numBytesRead <= bufferLength); sink.Write(buffer, 1, numBytesRead); return numBytesRead; }); } #if NETCOREAPP || NETSTANDARD2_1 [Theory] [PropertyData(nameof(ReadWriteTestBufferLengths))] public async Task ReadAsync_Memory(int bufferLength) { // Tests TranscodingStream.ReadAsync(Memory, CancellationToken) byte[] buffer = new byte[bufferLength]; await RunReadTestAsync(async (transcodingStream, cancellationToken, sink) => { int numBytesRead = await transcodingStream.ReadAsync(buffer.AsMemory(), cancellationToken); Assert.True(numBytesRead >= 0); Assert.True(numBytesRead <= bufferLength); sink.Write(buffer.AsSpan(0, numBytesRead)); return numBytesRead; }); } #endif [Fact] public async Task ReadAsync_LoopsWhenPartialDataReceived() { // Validates that the TranscodingStream will loop instead of returning 0 // if the inner stream read partial data and GetBytes cannot make forward progress. using AsyncComms comms = new(); Stream transcodingStream = new TranscodingStream(comms.ReadStream, Encoding.UTF8, Encoding.UTF8); // First, ensure that writing [ C0 ] (always invalid UTF-8) to the stream // causes the reader to return immediately with fallback behavior. byte[] readBuffer = new byte[1024]; comms.WriteBytes(new byte[] { 0xC0 }); int numBytesRead = await transcodingStream.ReadAsync(readBuffer, 0, readBuffer.Length); Assert.Equal(new byte[] { 0xEF, 0xBF, 0xBD }, readBuffer.AsSpan(0, numBytesRead).ToArray()); // fallback substitution // Next, ensure that writing [ C2 ] (partial UTF-8, needs more data) to the stream // causes the reader to asynchronously loop, returning "not yet complete". readBuffer = new byte[1024]; comms.WriteBytes(new byte[] { 0xC2 }); var task = transcodingStream.ReadAsync(readBuffer, 0, readBuffer.Length); Assert.False(task.IsCompleted); comms.WriteBytes(new byte[] { 0x80 }); // [ C2 80 ] is valid UTF-8 numBytesRead = await task; // should complete successfully Assert.Equal(new byte[] { 0xC2, 0x80 }, readBuffer.AsSpan(0, numBytesRead).ToArray()); // Finally, ensure that writing [ C2 ] (partial UTF-8, needs more data) to the stream // followed by EOF causes the reader to perform substitution before returning EOF. readBuffer = new byte[1024]; comms.WriteBytes(new byte[] { 0xC2 }); task = transcodingStream.ReadAsync(readBuffer, 0, readBuffer.Length); Assert.False(task.IsCompleted); comms.WriteEof(); numBytesRead = await task; // should complete successfully Assert.Equal(new byte[] { 0xEF, 0xBF, 0xBD }, readBuffer.AsSpan(0, numBytesRead).ToArray()); // fallback substitution // Next call really should return "EOF reached" readBuffer = new byte[1024]; Assert.Equal(0, await transcodingStream.ReadAsync(readBuffer, 0, readBuffer.Length)); } [Fact] public void ReadAsync_WithInvalidArgs_Throws() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); Assert.ThrowsArgumentNull(() => { transcodingStream.ReadAsync(null, 0, 0); }, "buffer"); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[5], -1, -1)); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[5], 3, -1)); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[5], 5, 1)); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[5], 6, -1)); Assert.Throws(() => (object)transcodingStream.ReadAsync(new byte[5], 6, 0)); } [Fact] public void ReadApm_WithInvalidArgs_ThrowsAsync() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); #if true // Not overriding BeginRead and base Stream's method returns a Task as its IAsyncResult, delaying parameter checks. Assert.ThrowsArgumentNull(() => transcodingStream.EndRead(transcodingStream.BeginRead(null, 0, 0, null, null)), "buffer"); Assert.Throws(() => transcodingStream.EndRead(transcodingStream.BeginRead(new byte[5], -1, -1, null, null))); Assert.Throws(() => transcodingStream.EndRead(transcodingStream.BeginRead(new byte[5], 3, -1, null, null))); Assert.Throws(() => transcodingStream.EndRead(transcodingStream.BeginRead(new byte[5], 5, 1, null, null))); Assert.Throws(() => transcodingStream.EndRead(transcodingStream.BeginRead(new byte[5], 6, -1, null, null))); Assert.Throws(() => transcodingStream.EndRead(transcodingStream.BeginRead(new byte[5], 6, 0, null, null))); #else Assert.ThrowsArgumentNull(() => transcodingStream.BeginRead(null, 0, 0, null, null), "buffer"); Assert.Throws(() => transcodingStream.BeginRead(new byte[5], -1, -1, null, null)); Assert.Throws(() => transcodingStream.BeginRead(new byte[5], 3, -1, null, null)); Assert.Throws(() => transcodingStream.BeginRead(new byte[5], 5, 1, null, null)); Assert.Throws(() => transcodingStream.BeginRead(new byte[5], 6, -1, null, null)); Assert.Throws(() => transcodingStream.BeginRead(new byte[5], 6, 0, null, null)); #endif } private async Task RunReadTestAsync(Func> callback, bool suppressExpectedCancellationTokenAsserts = false) { CancellationToken expectedCancellationToken = new CancellationTokenSource().Token; MemoryStream sink = new(); MemoryStream innerStream = new(); var delegatingInnerStreamMock = new Mock(MockBehavior.Strict); delegatingInnerStreamMock.Setup(o => o.CanRead).Returns(true); // Needed for ReadByte calls. delegatingInnerStreamMock.Setup(o => o.Read(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(innerStream.Read); #if true // In current src/ projects, always pass byte array to inner Stream. if (suppressExpectedCancellationTokenAsserts) { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(innerStream.ReadAsync); } else { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), expectedCancellationToken)) .Returns(innerStream.ReadAsync); } #else if (suppressExpectedCancellationTokenAsserts) { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny>(), It.IsAny())) .Returns, CancellationToken>(innerStream.ReadAsync); } else { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny>(), expectedCancellationToken)) .Returns, CancellationToken>(innerStream.ReadAsync); } #endif Stream transcodingStream = new TranscodingStream( innerStream: delegatingInnerStreamMock.Object, innerEncoding: Encoding.UTF8, thisEncoding: CustomAsciiEncoding); // Test with a small string, then test with a large string await RunOneTestIteration(128); await RunOneTestIteration(10 * 1024 * 1024); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF // Now put some invalid data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC0); // [ C0 ] is never valid in UTF-8 innerStream.Position = 0; sink.SetLength(0); // reset int numBytesReadJustNow; do { numBytesReadJustNow = await callback(transcodingStream, expectedCancellationToken, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF // Now put some incomplete data into the inner stream, followed by EOF, and ensure we get U+FFFD back out. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC2); // [ C2 ] must be followed by [ 80..BF ] in UTF-8 innerStream.Position = 0; sink.SetLength(0); // reset do { numBytesReadJustNow = await callback(transcodingStream, expectedCancellationToken, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF async Task RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset string expectedStringContents = GetVeryLongAsciiString(stringLength); innerStream.SetLength(0); // reset var bytes = Encoding.UTF8.GetBytes(expectedStringContents); innerStream.Write(bytes, 0, bytes.Length); innerStream.Position = 0; int numBytesReadJustNow; do { numBytesReadJustNow = await callback(transcodingStream, expectedCancellationToken, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal(expectedStringContents, ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); } } [Fact] public void ReadTimeout_WriteTimeout_NotSupported() { // Arrange - allow inner stream to support ReadTimeout + WriteTimeout var innerStreamMock = new Mock(); innerStreamMock.SetupProperty(o => o.ReadTimeout); innerStreamMock.SetupProperty(o => o.WriteTimeout); Stream transcodingStream = new TranscodingStream(Stream.Null, Encoding.UTF8, Encoding.UTF8, leaveOpen: true); // Act & assert - TranscodingStream shouldn't support ReadTimeout + WriteTimeout Assert.False(transcodingStream.CanTimeout); Assert.Throws(() => transcodingStream.ReadTimeout); Assert.Throws(() => transcodingStream.ReadTimeout = 42); Assert.Throws(() => transcodingStream.WriteTimeout); Assert.Throws(() => transcodingStream.WriteTimeout = 42); } [Fact] public void Seek_AlwaysThrows() { // MemoryStream is seekable, but we're not Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); Assert.False(transcodingStream.CanSeek); Assert.Throws(() => transcodingStream.Length); Assert.Throws(() => transcodingStream.Position); Assert.Throws(() => transcodingStream.Position = 0); Assert.Throws(() => transcodingStream.Seek(0, SeekOrigin.Current)); Assert.Throws(() => transcodingStream.SetLength(0)); } [Fact] public void Write() { MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream( innerStream, innerEncoding: ErrorCheckingUnicodeEncoding /* throws on error */, thisEncoding: Encoding.UTF8 /* performs substitution */, leaveOpen: true); // First, test Write(byte[], int, int) transcodingStream.Write(Encoding.UTF8.GetBytes("abcdefg"), 2, 3); Assert.Equal("cde", ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); // Then test WriteByte(byte) transcodingStream.WriteByte((byte)'z'); Assert.Equal("cdez", ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); // We'll write U+00E0 (utf-8: [C3 A0]) byte-by-byte. // We shouldn't flush any intermediate bytes. transcodingStream.WriteByte((byte)0xC3); Assert.Equal("cdez", ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); transcodingStream.WriteByte((byte)0xA0); Assert.Equal("cdez\u00E0", ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); innerStream.SetLength(0); // reset inner stream // Then test Write(ROS), once with a short string and once with a long string string asciiString = GetVeryLongAsciiString(128); byte[] asciiBytesAsUtf8 = Encoding.UTF8.GetBytes(asciiString); transcodingStream.Write(asciiBytesAsUtf8, 0, asciiBytesAsUtf8.Length); Assert.Equal(asciiString, ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); innerStream.SetLength(0); // reset inner stream asciiString = GetVeryLongAsciiString(16 * 1024 * 1024); asciiBytesAsUtf8 = Encoding.UTF8.GetBytes(asciiString); transcodingStream.Write(asciiBytesAsUtf8, 0, asciiBytesAsUtf8.Length); Assert.Equal(asciiString, ErrorCheckingUnicodeEncoding.GetString(innerStream.ToArray())); innerStream.SetLength(0); // reset inner stream // Close the outer stream and ensure no leftover data was written to the inner stream transcodingStream.Close(); Assert.Equal(0, innerStream.Position); } [Fact] public void Write_WithPartialData() { MemoryStream innerStream = new(); Stream transcodingStream = new TranscodingStream( innerStream, innerEncoding: CustomAsciiEncoding /* performs custom substitution */, thisEncoding: Encoding.UTF8 /* performs U+FFFD substitution */, leaveOpen: true); // First, write some incomplete data transcodingStream.Write(new byte[] { 0x78, 0x79, 0x7A, 0xC3 }, 0, 4); // [C3] shouldn't be flushed yet Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Flushing should have no effect transcodingStream.Flush(); Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Provide the second byte of the multi-byte sequence transcodingStream.WriteByte(0xA0); // [C3 A0] = U+00E0 Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Provide an incomplete sequence, then close the stream. // Closing the stream should flush the underlying buffers and write the replacement char. transcodingStream.Write(new byte[] { 0xE0, 0xBF }, 0, 1); // first 2 bytes of incomplete 3-byte sequence Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // wasn't flushed yet transcodingStream.Close(); Assert.Equal("xyz[00E0][FFFD]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); } [Fact] public void Write_WithInvalidArgs_Throws() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); Assert.ThrowsArgumentNull(() => transcodingStream.Write(null, 0, 0), "buffer"); Assert.Throws(() => transcodingStream.Write(new byte[5], -1, -1)); Assert.Throws(() => transcodingStream.Write(new byte[5], 3, -1)); Assert.Throws(() => transcodingStream.Write(new byte[5], 5, 1)); Assert.Throws(() => transcodingStream.Write(new byte[5], 6, -1)); Assert.Throws(() => transcodingStream.Write(new byte[5], 6, 0)); } // Moq heavily utilizes RefEmit, which does not work on most aot workloads [Fact] public async Task WriteAsync_WithFullData() { MemoryStream sink = new(); CancellationToken expectedFlushAsyncCancellationToken = new CancellationTokenSource().Token; CancellationToken expectedWriteAsyncCancellationToken = new CancellationTokenSource().Token; var innerStreamMock = new Mock(MockBehavior.Strict); innerStreamMock.Setup(o => o.CanWrite).Returns(true); #if true // In current src/ projects, always pass byte array to inner Stream. innerStreamMock.Setup(o => o.WriteAsync(It.IsAny(), It.IsAny(), It.IsAny(), expectedWriteAsyncCancellationToken)) .Returns(sink.WriteAsync); #else innerStreamMock.Setup(o => o.WriteAsync(It.IsAny>(), expectedWriteAsyncCancellationToken)) .Returns, CancellationToken>(sink.WriteAsync); #endif innerStreamMock.Setup(o => o.FlushAsync(expectedFlushAsyncCancellationToken)).Returns(TaskHelpers.Completed()); Stream transcodingStream = new TranscodingStream( innerStreamMock.Object, innerEncoding: ErrorCheckingUnicodeEncoding, thisEncoding: Encoding.UTF8 /* performs U+FFFD substitution */, leaveOpen: true); // First, test WriteAsync(byte[], int, int, CancellationToken) await transcodingStream.WriteAsync(Encoding.UTF8.GetBytes("abcdefg"), 2, 3, expectedWriteAsyncCancellationToken); Assert.Equal("cde", ErrorCheckingUnicodeEncoding.GetString(sink.ToArray())); // We'll write U+00E0 (utf-8: [C3 A0]) byte-by-byte. // We shouldn't flush any intermediate bytes. await transcodingStream.WriteAsync(new byte[] { 0xC3, 0xA0 }, 0, 1, expectedWriteAsyncCancellationToken); await transcodingStream.FlushAsync(expectedFlushAsyncCancellationToken); Assert.Equal("cde", ErrorCheckingUnicodeEncoding.GetString(sink.ToArray())); await transcodingStream.WriteAsync(new byte[] { 0xC3, 0xA0 }, 1, 1, expectedWriteAsyncCancellationToken); Assert.Equal("cde\u00E0", ErrorCheckingUnicodeEncoding.GetString(sink.ToArray())); sink.SetLength(0); // reset sink // Then test WriteAsync(ROM, CancellationToken), once with a short string and once with a long string string asciiString = GetVeryLongAsciiString(128); byte[] asciiBytesAsUtf8 = Encoding.UTF8.GetBytes(asciiString); await transcodingStream.WriteAsync(asciiBytesAsUtf8, 0, asciiBytesAsUtf8.Length, expectedWriteAsyncCancellationToken); Assert.Equal(asciiString, ErrorCheckingUnicodeEncoding.GetString(sink.ToArray())); sink.SetLength(0); // reset sink asciiString = GetVeryLongAsciiString(16 * 1024 * 1024); asciiBytesAsUtf8 = Encoding.UTF8.GetBytes(asciiString); await transcodingStream.WriteAsync(asciiBytesAsUtf8, 0, asciiBytesAsUtf8.Length, expectedWriteAsyncCancellationToken); Assert.Equal(asciiString, ErrorCheckingUnicodeEncoding.GetString(sink.ToArray())); sink.SetLength(0); // reset sink // Close the outer stream and ensure no leftover data was written to the inner stream transcodingStream.Dispose(); Assert.Equal(0, sink.Position); } // Moq heavily utilizes RefEmit, which does not work on most aot workloads [Fact] public async Task WriteAsync_WithPartialData() { MemoryStream sink = new(); CancellationToken expectedCancellationToken = new CancellationTokenSource().Token; var innerStreamMock = new Mock(MockBehavior.Strict); innerStreamMock.Setup(o => o.CanWrite).Returns(true); #if true // In current src/ projects, always pass byte array to inner Stream. innerStreamMock.Setup(o => o.WriteAsync(It.IsAny(), It.IsAny(), It.IsAny(), expectedCancellationToken)) .Returns(sink.WriteAsync); #else innerStreamMock.Setup(o => o.WriteAsync(It.IsAny>(), expectedCancellationToken)) .Returns, CancellationToken>(sink.WriteAsync); #endif Stream transcodingStream = new TranscodingStream( innerStreamMock.Object, innerEncoding: CustomAsciiEncoding /* performs custom substitution */, thisEncoding: Encoding.UTF8 /* performs U+FFFD substitution */, leaveOpen: true); // First, write some incomplete data await transcodingStream.WriteAsync(new byte[] { 0x78, 0x79, 0x7A, 0xC3 }, 0, 4, expectedCancellationToken); // [C3] shouldn't be flushed yet Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // Provide the second byte of the multi-byte sequence await transcodingStream.WriteAsync(new byte[] { 0xA0 }, 0, 1, expectedCancellationToken); // [C3 A0] = U+00E0 Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // Provide an incomplete sequence, then close the stream. // Closing the stream should flush the underlying buffers and write the replacement char. await transcodingStream.WriteAsync(new byte[] { 0xE0, 0xBF }, 0, 2, expectedCancellationToken); // first 2 bytes of incomplete 3-byte sequence Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // wasn't flushed yet // The call to Dispose() will call innerStream.Write. innerStreamMock.Setup(o => o.Write(It.IsAny(), It.IsAny(), It.IsAny())) .Callback(sink.Write); transcodingStream.Dispose(); Assert.Equal("xyz[00E0][FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); } [Fact] public void WriteAsync_WithInvalidArgs_Throws() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); Assert.ThrowsArgumentNull(() => { transcodingStream.WriteAsync(null, 0, 0); }, "buffer"); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[5], -1, -1)); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[5], 3, -1)); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[5], 5, 1)); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[5], 6, -1)); Assert.Throws(() => (object)transcodingStream.WriteAsync(new byte[5], 6, 0)); } // Moq heavily utilizes RefEmit, which does not work on most aot workloads [Fact] public void WriteApm() { // Arrange MemoryStream sink = new(); object expectedState = new(); var innerStreamMock = new Mock(MockBehavior.Strict); innerStreamMock.Setup(o => o.CanWrite).Returns(true); #if true // In current src/ projects, base Stream's BeginWrite method relies on Write and passes the byte array to the inner stream. innerStreamMock.Setup(o => o.Write(It.IsAny(), It.IsAny(), It.IsAny())) .Callback(sink.Write); #else innerStreamMock.Setup(o => o.WriteAsync(It.IsAny>(), CancellationToken.None)) .Returns, CancellationToken>(sink.WriteAsync); #endif Stream transcodingStream = new TranscodingStream(innerStreamMock.Object, Encoding.UTF8, Encoding.UTF8); // Act IAsyncResult asyncResult = transcodingStream.BeginWrite(Encoding.UTF8.GetBytes("abcdefg"), 1, 3, null, expectedState); transcodingStream.EndWrite(asyncResult); // Assert Assert.Equal(expectedState, asyncResult.AsyncState); Assert.Equal("bcd", Encoding.UTF8.GetString(sink.ToArray())); } [Fact] public void WriteApm_WithInvalidArgs_Throws() { Stream transcodingStream = new TranscodingStream(new MemoryStream(), Encoding.UTF8, Encoding.UTF8); #if true // Not overriding BeginRead and base Stream's method returns a Task as its IAsyncResult, delaying parameter checks. Assert.ThrowsArgumentNull(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(null, 0, 0, null, null)), "buffer"); Assert.Throws(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(new byte[5], -1, -1, null, null))); Assert.Throws(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(new byte[5], 3, -1, null, null))); Assert.Throws(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(new byte[5], 5, 1, null, null))); Assert.Throws(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(new byte[5], 6, -1, null, null))); Assert.Throws(() => transcodingStream.EndWrite(transcodingStream.BeginWrite(new byte[5], 6, 0, null, null))); #else Assert.ThrowsArgumentNull(() => transcodingStream.BeginWrite(null, 0, 0, null, null), "buffer"); Assert.Throws(() => transcodingStream.BeginWrite(new byte[5], -1, -1, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[5], 3, -1, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[5], 5, 1, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[5], 6, -1, null, null)); Assert.Throws(() => transcodingStream.BeginWrite(new byte[5], 6, 0, null, null)); #endif } // returns "abc...xyzabc...xyzabc..." private static string GetVeryLongAsciiString(int length) { #if NETCOREAPP || NETSTANDARD2_1 return string.Create(length, (object)null, (buffer, _) => { for (int i = 0; i < buffer.Length; i++) { buffer[i] = (char)('a' + (i % 26)); } }); #else // Somewhat minor that the string just repeats a single character. return new string('z', length); #endif } // A custom ASCIIEncoding where both encoder + decoder fallbacks have been specified private static readonly Encoding CustomAsciiEncoding = Encoding.GetEncoding( "ascii", new CustomEncoderFallback(), new DecoderReplacementFallback("\uFFFD")); private static readonly Encoding ErrorCheckingAsciiEncoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); private static readonly UnicodeEncoding ErrorCheckingUnicodeEncoding = new(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: true); // A custom encoder fallback which substitutes unknown chars with "[xxxx]" (the code point as hex) private sealed class CustomEncoderFallback : EncoderFallback { public override int MaxCharCount => 8; // = "[10FFFF]".Length public override EncoderFallbackBuffer CreateFallbackBuffer() { return new CustomEncoderFallbackBuffer(); } private sealed class CustomEncoderFallbackBuffer : EncoderFallbackBuffer { private string _remaining = string.Empty; private int _remainingIdx = 0; public override int Remaining => _remaining.Length - _remainingIdx; public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) => FallbackCommon((uint)char.ConvertToUtf32(charUnknownHigh, charUnknownLow)); public override bool Fallback(char charUnknown, int index) => FallbackCommon(charUnknown); private bool FallbackCommon(uint codePoint) { Assert.True(codePoint <= 0x10FFFF); _remaining = String.Format(CultureInfo.InvariantCulture, "[{0:X4}]", codePoint); _remainingIdx = 0; return true; } public override char GetNextChar() { return (_remainingIdx < _remaining.Length) ? _remaining[_remainingIdx++] : '\0' /* end of string reached */; } public override bool MovePrevious() { if (_remainingIdx == 0) { return false; } _remainingIdx--; return true; } } } /// A custom encoding that's used to roundtrip from bytes to bytes through a string. private sealed class IdentityEncoding : Encoding { public override int GetByteCount(char[] chars, int index, int count) => count; public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { Span span = chars.AsSpan(charIndex, charCount); for (int i = 0; i < span.Length; i++) { Debug.Assert(span[i] <= 0xFF); bytes[byteIndex + i] = (byte)span[i]; } return charCount; } public override int GetCharCount(byte[] bytes, int index, int count) => count; public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { Span span = bytes.AsSpan(byteIndex, byteCount); for (int i = 0; i < span.Length; i++) { Debug.Assert(span[i] <= 0xFF); chars[charIndex + i] = (char)span[i]; } return byteCount; } public override int GetMaxByteCount(int charCount) => charCount; public override int GetMaxCharCount(int byteCount) => byteCount; public override byte[] GetPreamble() => Array.Empty(); } // A helper type that allows synchronously writing to a stream while asynchronously // reading from it. private sealed class AsyncComms : IDisposable { private readonly BlockingCollection _blockingCollection; private readonly PipeWriter _writer; public AsyncComms() { _blockingCollection = new BlockingCollection(); var pipe = new Pipe(); ReadStream = pipe.Reader.AsStream(); _writer = pipe.Writer; Task.Run(DrainWorker); } public Stream ReadStream { get; } public void Dispose() { _blockingCollection.Dispose(); } public void WriteBytes(ReadOnlySpan bytes) { _blockingCollection.Add(bytes.ToArray()); } public void WriteEof() { _blockingCollection.Add(null); } private async Task DrainWorker() { byte[] buffer; while ((buffer = _blockingCollection.Take()) is not null) { await _writer.WriteAsync(buffer); } _writer.Complete(); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/InvalidByteRangeExceptionTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class InvalidByteRangeExceptionTest { [Fact] public void Ctor_ThrowsOnNullRange() { Assert.ThrowsArgumentNull(() => new InvalidByteRangeException(contentRange: null), "contentRange"); } [Fact] public void Ctor_SetsContentRange() { ContentRangeHeaderValue contentRange = new ContentRangeHeaderValue(0, 20, 100); InvalidByteRangeException invalidByteRangeException = new InvalidByteRangeException(contentRange); Assert.Same(contentRange, invalidByteRangeException.ContentRange); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MimeBodyPartTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class MimeBodyPartTest { public static TheoryDataSet BadMultipartStreamProviders { get { return new TheoryDataSet { new HttpContentMultipartExtensionsTests.NullProvider(), new HttpContentMultipartExtensionsTests.BadStreamProvider(), new HttpContentMultipartExtensionsTests.ReadOnlyStreamProvider(), }; } } [Theory] [TestDataSet(typeof(MimeBodyPartTest), "BadMultipartStreamProviders")] public async Task GetOutputStream_ThrowsOnInvalidStreamProvider(MultipartStreamProvider streamProvider) { // Arrange HttpContent parent = new StringContent("hello"); MimeBodyPart bodypart = new MimeBodyPart(streamProvider, 1024, parent); bodypart.Segments.Add(new ArraySegment(new byte[] { 1 })); // Act and Assert await Assert.ThrowsAsync(() => bodypart.WriteSegment(bodypart.Segments[0], CancellationToken.None)); } [Fact] public async Task Dispose_ClosesOutputStreamOnNonMemoryStream() { // Arrange HttpContent parent = new StringContent("hello"); Mock mockStream = new Mock() { CallBase = true }; mockStream.Setup(s => s.CanWrite).Returns(true); Mock mockStreamProvider = new Mock(); mockStreamProvider.Setup(sp => sp.GetStream(It.IsAny(), It.IsAny())).Returns(mockStream.Object); MimeBodyPart bodypart = new MimeBodyPart(mockStreamProvider.Object, 1024, parent); bodypart.Segments.Add(new ArraySegment(new byte[] { 1 })); await bodypart.WriteSegment(bodypart.Segments[0], CancellationToken.None); bodypart.IsComplete = true; // Act bodypart.GetCompletedHttpContent(); bodypart.Dispose(); // Assert mockStream.Verify(s => s.Close(), Times.Once()); } [Fact] public async Task Dispose_SetsPositionToZeroOnMemoryStream() { // Arrange HttpContent parent = new StringContent("hello"); Mock mockStream = new Mock { CallBase = true }; Mock mockStreamProvider = new Mock(); mockStreamProvider.Setup(sp => sp.GetStream(It.IsAny(), It.IsAny())).Returns(mockStream.Object); MimeBodyPart bodypart = new MimeBodyPart(mockStreamProvider.Object, 1024, parent); bodypart.Segments.Add(new ArraySegment(new byte[] { 1 })); await bodypart.WriteSegment(bodypart.Segments[0], CancellationToken.None); bodypart.IsComplete = true; // Act bodypart.GetCompletedHttpContent(); bodypart.Dispose(); // Assert mockStream.VerifySet(s => s.Position = 0); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockAsyncCallback.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Formatting.Mocks { public class MockAsyncCallback { public bool WasInvoked { get; private set; } public IAsyncResult AsyncResult { get; private set; } public void Handler(IAsyncResult result) { WasInvoked = true; AsyncResult = result; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockCompletedAsyncResult.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Moq; namespace System.Net.Http.Formatting.Mocks { public class MockCompletedAsyncResult { private MockCompletedAsyncResult() { } public static IAsyncResult Create(bool completedSynchronously, object userState) { Mock mockIAsyncResult = new Mock(); mockIAsyncResult.Setup(ar => ar.AsyncState).Returns(userState); mockIAsyncResult.Setup(ar => ar.IsCompleted).Returns(true); mockIAsyncResult.Setup(ar => ar.CompletedSynchronously).Returns(completedSynchronously); return mockIAsyncResult.Object; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockContentNegotiator.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.ObjectModel; using System.Net.Http.Headers; using System.Text; namespace System.Net.Http.Formatting.Mocks { public class MockContentNegotiator : DefaultContentNegotiator { public MockContentNegotiator() { } public MockContentNegotiator(bool excludeMatchOnTypeOnly) : base(excludeMatchOnTypeOnly) { } public new Collection ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable formatters) { return base.ComputeFormatterMatches(type, request, formatters); } public new MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection matches) { return base.SelectResponseMediaTypeFormatter(matches); } public new Encoding SelectResponseCharacterEncoding(HttpRequestMessage request, MediaTypeFormatter formatter) { return base.SelectResponseCharacterEncoding(request, formatter); } public new MediaTypeFormatterMatch MatchMediaTypeMapping(HttpRequestMessage request, MediaTypeFormatter formatter) { return base.MatchMediaTypeMapping(request, formatter); } public new MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable sortedAcceptValues, MediaTypeFormatter formatter) { return base.MatchAcceptHeader(sortedAcceptValues, formatter); } public new MediaTypeFormatterMatch MatchRequestMediaType(HttpRequestMessage request, MediaTypeFormatter formatter) { return base.MatchRequestMediaType(request, formatter); } public new bool ShouldMatchOnType(IEnumerable sortedAcceptValues) { return base.ShouldMatchOnType(sortedAcceptValues); } public new MediaTypeFormatterMatch MatchType(Type type, MediaTypeFormatter formatter) { return base.MatchType(type, formatter); } public new IEnumerable SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection headerValues) { return base.SortMediaTypeWithQualityHeaderValuesByQFactor(headerValues); } public new IEnumerable SortStringWithQualityHeaderValuesByQFactor(ICollection headerValues) { return base.SortStringWithQualityHeaderValuesByQFactor(headerValues); } public new MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement) { return base.UpdateBestMatch(current, potentialReplacement); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockDelegatingHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; using System.Threading.Tasks; namespace System.Net.Http.Mocks { internal class MockDelegatingHandler : DelegatingHandler { private bool _throwInSendAsync; public MockDelegatingHandler(bool throwInSendAsync = false) { _throwInSendAsync = throwInSendAsync; } public MockDelegatingHandler(HttpMessageHandler innerHandler, bool throwInSendAsync = false) : base(innerHandler) { _throwInSendAsync = throwInSendAsync; } public bool WasInvoked { get; private set; } public HttpRequestMessage Request { get; private set; } public CancellationToken CancellationToken { get; private set; } public Exception SendAsyncException { get; private set; } protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { WasInvoked = true; Request = request; CancellationToken = cancellationToken; if (_throwInSendAsync) { SendAsyncException = new Exception("SendAsync exception"); throw SendAsyncException; } return Task.FromResult(request.CreateResponse()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockDelegatingStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Internal; namespace System.Net.Http.Mocks { internal class MockDelegatingStream : DelegatingStream { public MockDelegatingStream(Stream innerStream) : base(innerStream) { } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockHttpContent.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using System.Threading.Tasks; namespace System.Net.Http.Formatting.Mocks { public delegate bool TryComputeLengthDelegate(out long length); public class MockHttpContent : HttpContent { public MockHttpContent() { } public MockHttpContent(HttpContent innerContent) { InnerContent = innerContent; Headers.ContentType = innerContent.Headers.ContentType; } public MockHttpContent(MediaTypeHeaderValue contentType) { if (contentType == null) { throw new ArgumentNullException("contentType"); } Headers.ContentType = contentType; } public MockHttpContent(string contentType) { if (String.IsNullOrWhiteSpace(contentType)) { throw new ArgumentNullException("contentType"); } Headers.ContentType = new MediaTypeHeaderValue(contentType); } public HttpContent InnerContent { get; set; } public Action DisposeCallback { get; set; } public TryComputeLengthDelegate TryComputeLengthCallback { get; set; } public Action SerializeToStreamCallback { get; set; } public Func SerializeToStreamAsyncCallback { get; set; } protected override void Dispose(bool disposing) { if (DisposeCallback != null) { DisposeCallback(disposing); } base.Dispose(disposing); } protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) { if (SerializeToStreamAsyncCallback != null) { return SerializeToStreamAsyncCallback(stream, context); } else if (InnerContent != null) { return InnerContent.CopyToAsync(stream, context); } else { throw new InvalidOperationException("Construct with inner HttpContent or set SerializeToStreamCallback first."); } } protected override bool TryComputeLength(out long length) { if (TryComputeLengthCallback != null) { return TryComputeLengthCallback(out length); } if (InnerContent != null) { long? len = InnerContent.Headers.ContentLength; length = len.HasValue ? len.Value : 0L; return len.HasValue; } length = 0L; return false; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockMediaTypeFormatter.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Text; namespace System.Net.Http.Formatting.Mocks { public class MockMediaTypeFormatter : MediaTypeFormatter { private bool _canWriteAnyTypes = true; public bool CallBase { get; set; } public Func CanReadTypeCallback { get; set; } public Func CanWriteTypeCallback { get; set; } internal override bool CanWriteAnyTypes { get { return _canWriteAnyTypes; } } public bool CanWriteAnyTypesReturn { get { return _canWriteAnyTypes; } set { _canWriteAnyTypes = value; } } public override bool CanReadType(Type type) { if (!CallBase && CanReadTypeCallback == null) { throw new InvalidOperationException("CallBase or CanReadTypeCallback must be set first."); } return CanReadTypeCallback != null ? CanReadTypeCallback(type) : true; } public override bool CanWriteType(Type type) { if (!CallBase && CanWriteTypeCallback == null) { throw new InvalidOperationException("CallBase or CanWriteTypeCallback must be set first."); } return CanWriteTypeCallback != null ? CanWriteTypeCallback(type) : true; } public new Encoding SelectCharacterEncoding(HttpContentHeaders contentHeaders) { return base.SelectCharacterEncoding(contentHeaders); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockMediaTypeMapping.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; namespace System.Net.Http.Formatting.Mocks { public class MockMediaTypeMapping : MediaTypeMapping { public MockMediaTypeMapping(string mediaType, double matchQuality) : base(mediaType) { MatchQuality = matchQuality; } public MockMediaTypeMapping(MediaTypeHeaderValue mediaType, double matchQuality) : base(mediaType) { MatchQuality = matchQuality; } public double MatchQuality { get; private set; } public HttpRequestMessage Request { get; private set; } public bool WasInvoked { get; private set; } public override double TryMatchMediaType(HttpRequestMessage request) { WasInvoked = true; Request = request; return MatchQuality; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockNonClosingDelegatingStream.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Internal; namespace System.Net.Http.Mocks { internal class MockNonClosingDelegatingStream : NonClosingDelegatingStream { public MockNonClosingDelegatingStream(Stream innerStream) : base(innerStream) { } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/MockProgressEventHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. namespace System.Net.Http.Handlers { public class MockProgressEventHandler { public bool WasInvoked { get; private set; } public object Sender { get; private set; } public HttpProgressEventArgs EventArgs { get; private set; } public void Handler(object sender, HttpProgressEventArgs eventArgs) { WasInvoked = true; Sender = sender; EventArgs = eventArgs; } public static ProgressMessageHandler CreateProgressMessageHandler(out MockProgressEventHandler progressEventHandler, bool sendProgress) { ProgressMessageHandler progressHandler = new ProgressMessageHandler(); progressEventHandler = new MockProgressEventHandler(); if (sendProgress) { progressHandler.HttpSendProgress += progressEventHandler.Handler; } else { progressHandler.HttpReceiveProgress += progressEventHandler.Handler; } return progressHandler; } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/Mocks/TestableHttpMessageHandler.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; using System.Threading.Tasks; namespace System.Net.Http.Mocks { public class TestableHttpMessageHandler : HttpMessageHandler { public virtual Task SendAsyncPublic(HttpRequestMessage request, CancellationToken cancellationToken) { throw new NotImplementedException(); } protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return SendAsyncPublic(request, cancellationToken); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartFileDataTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class MultipartFileDataTest { [Fact] public void Constructor_ThrowsOnNullHeaders() { Assert.ThrowsArgumentNull(() => new MultipartFileData(null, "file"), "headers"); } [Fact] public void Constructor_ThrowsOnNullLocalFileName() { HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); Assert.ThrowsArgumentNull(() => new MultipartFileData(headers, null), "localFileName"); } [Fact] public void Constructor_InitializesCorrectly() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); string fileName = "filename"; // Act MultipartFileData fileData = new MultipartFileData(headers, fileName); Assert.Same(headers, fileData.Headers); Assert.Same(fileName, fileData.LocalFileName); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartFileStreamProviderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Linq; using System.Net.Http.Internal; using Microsoft.TestCommon; namespace System.Net.Http { public class MockMultipartFileStreamProvider : MultipartFileStreamProvider { public MockMultipartFileStreamProvider() : base(Path.GetTempPath()) { } public MockMultipartFileStreamProvider(string rootPath) : base(rootPath) { } public MockMultipartFileStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize) { } } public class MultipartFileStreamProviderTests : MultipartStreamProviderTestBase { private const int MinBufferSize = 1; private const int ValidBufferSize = 0x111; private const string ValidPath = @"c:\some\path"; #if !NETCOREAPP // .NET Core does not enforce path validity in many APIs. public static TheoryDataSet NotSupportedFilePaths { get { return new TheoryDataSet { "cc:\\a\\b", "123:\\a\\b", "c d:\\a\\b", }; } } #endif public static TheoryDataSet InvalidFilePaths { get { return new TheoryDataSet { "", " ", " ", #if !NETCOREAPP // .NET Core does not enforce path validity in many APIs. "\t\t \n ", "c:\\ab", "c:\\a\"b", "c:\\a\tb", "c:\\a|b", "c:\\a\bb", #endif "c:\\a\0b", "c :\\a\0b", }; } } [Fact] public void Constructor_ThrowsOnNullRootPath() { Assert.ThrowsArgumentNull(() => { new MultipartFileStreamProvider(null); }, "rootPath"); } #if !NETCOREAPP // .NET Core does not enforce path validity in many APIs. [Theory] [PropertyData("NotSupportedFilePaths")] public void Constructor_ThrowsOnNotSupportedRootPath(string notSupportedPath) { Assert.Throws(() => new MultipartFileStreamProvider(notSupportedPath, ValidBufferSize)); } #endif [Theory] [PropertyData("InvalidFilePaths")] public void Constructor_ThrowsOnInvalidRootPath(string invalidPath) { Assert.ThrowsArgument(() => new MultipartFileStreamProvider(invalidPath, ValidBufferSize), null); } [Fact] public void Constructor_InvalidBufferSize() { Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new MultipartFileStreamProvider(ValidPath, MinBufferSize - 1), "bufferSize", MinBufferSize.ToString(), MinBufferSize - 1); } [Fact] public void FileData_IsEmpty() { MultipartFileStreamProvider provider = new MultipartFileStreamProvider(ValidPath, ValidBufferSize); Assert.Empty(provider.FileData); } [Fact] public void GetStream() { Stream stream0 = null; Stream stream1 = null; try { string tempPath = Path.GetTempPath(); MultipartFormDataContent content = new MultipartFormDataContent(); content.Add(new StringContent("Content 1"), "NoFile"); content.Add(new StringContent("Content 2"), "File", "Filename"); MultipartFileStreamProvider provider = new MultipartFileStreamProvider(tempPath); stream0 = provider.GetStream(content, content.ElementAt(0).Headers); stream1 = provider.GetStream(content, content.ElementAt(1).Headers); Assert.IsType(stream0); Assert.IsType(stream1); Assert.Equal(2, provider.FileData.Count); string partialFileName = String.Format("{0}BodyPart_", tempPath); Assert.Contains(partialFileName, provider.FileData[0].LocalFileName); Assert.Contains(partialFileName, provider.FileData[1].LocalFileName); Assert.Same(content.ElementAt(0).Headers.ContentDisposition, provider.FileData[0].Headers.ContentDisposition); Assert.Same(content.ElementAt(1).Headers.ContentDisposition, provider.FileData[1].Headers.ContentDisposition); } finally { if (stream0 != null) { stream0.Close(); } if (stream1 != null) { stream1.Close(); } } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartFormDataRemoteStreamProviderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class MultipartFormDataRemoteStreamProviderTests : MultipartStreamProviderTestBase { [Fact] public void FileData_IsEmpty() { CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(); Assert.Empty(provider.FileData); } [Fact] public void FormData_IsEmpty() { CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(); Assert.Empty(provider.FormData); } [Fact] public void GetStream_ThrowsOnNoContentDisposition() { // Arrange CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(); HttpContent content = new StringContent(String.Empty); HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act & Assert Assert.Throws(() => { provider.GetStream(content, headers); }); } [Fact] public void GetStream() { // Arrange Stream stream0 = null; Stream stream1 = null; try { MultipartFormDataContent content = new MultipartFormDataContent(); content.Add(new StringContent("Content 1"), "NoFile"); content.Add(new StringContent("Content 2"), "File", "Filename"); CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(); // Act stream0 = provider.GetStream(content, content.ElementAt(0).Headers); stream1 = provider.GetStream(content, content.ElementAt(1).Headers); // Assert Assert.IsType(stream0); Assert.Single(provider.RemoteStreams, stream1); MultipartRemoteFileData fileData = Assert.Single(provider.FileData); string expectedUrl = provider.UrlBase + "Filename"; Assert.Equal(expectedUrl, fileData.Location); Assert.Same(content.ElementAt(1).Headers.ContentDisposition, fileData.Headers.ContentDisposition); } finally { if (stream0 != null) { stream0.Close(); } if (stream1 != null) { stream1.Close(); } } } [Fact] [ReplaceCulture] public void GetStream_StreamResultNullThrowsException() { // Arrange MultipartFormDataContent content = new MultipartFormDataContent(); content.Add(new StringContent("Content"), "File", "Filename"); CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(isResultNull: true); // Act & Assert Assert.Throws( () => provider.GetStream(content, content.ElementAt(0).Headers), "The 'GetRemoteStream' method in 'CustomMultipartFormDataRemoteStreamProvider' returned null. " + "It must return a RemoteStreamResult instance containing a writable stream and a valid URL." ); } [Fact] public async Task PostProcessing_ProcessesFormData() { // Arrange int maxContents = 16; string contentFormat = "Content {0}"; string formNameFormat = "FormName_{0}"; string fileNameFormat = "FileName_{0}"; MultipartFormDataContent multipartContent = new MultipartFormDataContent(); // Create half contents for form data and the other half for file data. for (int index = 0; index < maxContents; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); if (index < maxContents/2) { multipartContent.Add(new StringContent(content), formName); } else { string fileName = String.Format(fileNameFormat, index); multipartContent.Add(new StringContent(content), formName, fileName); } } CustomMultipartFormDataRemoteStreamProvider provider = new CustomMultipartFormDataRemoteStreamProvider(); foreach (HttpContent content in multipartContent) { provider.Contents.Add(content); using (provider.GetStream(multipartContent, content.Headers)) { } } // Act await provider.ExecutePostProcessingAsync(); // Assert Assert.Equal(maxContents/2, provider.FormData.Count); // half contents for form data for (int index = 0; index < maxContents/2; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); Assert.Equal(content, provider.FormData[formName]); } // the other half for file data HttpContent[] contents = multipartContent.ToArray(); for (int index = maxContents/2; index < maxContents; index++) { int fileDataIndex = index - (maxContents/2); string fileName = String.Format(fileNameFormat, index); string url = provider.UrlBase + fileName; Assert.Equal(url, provider.FileData[fileDataIndex].Location); Assert.Same(contents[index].Headers, provider.FileData[fileDataIndex].Headers); } } [Fact] public async Task ExecutePostProcessingAsyncWithoutCancellationToken_GetCalledBy_ReadAsMultipartAsync() { // Arrange MultipartFormDataContent multipartContent = new MultipartFormDataContent(); Mock mockProvider = new Mock(); mockProvider.CallBase = true; // Act await multipartContent.ReadAsMultipartAsync(mockProvider.Object); // Assert mockProvider.Verify(p => p.ExecutePostProcessingAsync(), Times.Once()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartFormDataStreamProviderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class MockMultipartFormDataStreamProvider : MultipartFormDataStreamProvider { public MockMultipartFormDataStreamProvider() : base(Path.GetTempPath()) { } public MockMultipartFormDataStreamProvider(string rootPath) : base(rootPath) { } public MockMultipartFormDataStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize) { } } public class MultipartFormDataStreamProviderTests : MultipartStreamProviderTestBase { private const int ValidBufferSize = 0x111; private const string ValidPath = @"c:\some\path"; [Fact] public void FormData_IsEmpty() { MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(ValidPath, ValidBufferSize); Assert.Empty(provider.FormData); } [Fact] public void GetStream_ThrowsOnNoContentDisposition() { MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(ValidPath); HttpContent content = new StringContent(String.Empty); HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); Assert.Throws(() => { provider.GetStream(content, headers); }); } [Fact] public void GetStream() { Stream stream0 = null; Stream stream1 = null; try { string tempPath = Path.GetTempPath(); MultipartFormDataContent content = new MultipartFormDataContent(); content.Add(new StringContent("Content 1"), "NoFile"); content.Add(new StringContent("Content 2"), "File", "Filename"); MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(tempPath); stream0 = provider.GetStream(content, content.ElementAt(0).Headers); stream1 = provider.GetStream(content, content.ElementAt(1).Headers); Assert.IsType(stream0); Assert.IsType(stream1); MultipartFileData fileData = Assert.Single(provider.FileData); string partialFileName = String.Format("{0}BodyPart_", tempPath); Assert.Contains(partialFileName, fileData.LocalFileName); Assert.Same(content.ElementAt(1).Headers.ContentDisposition, fileData.Headers.ContentDisposition); } finally { if (stream0 != null) { stream0.Close(); } if (stream1 != null) { stream1.Close(); } } } [Fact] public async Task PostProcessing_ProcessesFormData() { // Arrange int maxContents = 16; string contentFormat = "Content {0}"; string formNameFormat = "FormName_{0}"; MultipartFormDataContent multipartContent = new MultipartFormDataContent(); for (int index = 0; index < maxContents; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); multipartContent.Add(new StringContent(content), formName); } MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(ValidPath); foreach (HttpContent content in multipartContent) { provider.Contents.Add(content); provider.GetStream(multipartContent, content.Headers); } // Act await provider.ExecutePostProcessingAsync(); // Assert Assert.Equal(maxContents, provider.FormData.Count); for (int index = 0; index < maxContents; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); Assert.Equal(content, provider.FormData[formName]); } } [Fact] public async Task PostProcessing_ProcessesFormData_WithCustomMultipartFormDataStreamProvider() { // Arrange string tempPath = Path.GetTempPath(); int maxContents = 16; string contentFormat = "Content {0}"; string formNameFormat = "FormName_{0}"; string fileNameFormat = "FileName_{0}"; MultipartFormDataContent multipartContent = new MultipartFormDataContent(); // Create half contents for form data and the other half for file data. for (int index = 0; index < maxContents; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); if (index < maxContents/2) { multipartContent.Add(new StringContent(content), formName); } else { string fileName = String.Format(fileNameFormat, index); multipartContent.Add(new StringContent(content), formName, fileName); } } CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(tempPath); foreach (HttpContent content in multipartContent) { provider.Contents.Add(content); using (provider.GetStream(multipartContent, content.Headers)) { } } // Act await provider.ExecutePostProcessingAsync(); // Assert Assert.Equal(maxContents / 2, provider.FormData.Count); // half contents for form data for (int index = 0; index < maxContents / 2; index++) { string content = String.Format(contentFormat, index); string formName = String.Format(formNameFormat, index); Assert.Equal(content, provider.FormData[formName]); } // the other half for file data HttpContent[] contents = multipartContent.ToArray(); for (int index = maxContents / 2; index < maxContents; index++) { int fileDataIndex = index - (maxContents / 2); string fileName = String.Format(fileNameFormat, index); Assert.Equal(fileName, provider.FileData[fileDataIndex].LocalFileName); Assert.Same(contents[index].Headers, provider.FileData[fileDataIndex].Headers); } } [Fact] public async Task ExecutePostProcessingAsyncWithoutCancellationToken_GetCalledBy_ReadAsMultipartAsync() { // Arrange MultipartFormDataContent multipartContent = new MultipartFormDataContent(); Mock mockProvider = new Mock(ValidPath); mockProvider.CallBase = true; // Act var provider = await multipartContent.ReadAsMultipartAsync(mockProvider.Object); // Assert mockProvider.Verify(p => p.ExecutePostProcessingAsync(), Times.Once()); } private class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider { public CustomMultipartFormDataStreamProvider(string rootPath) : base(rootPath) { } public CustomMultipartFormDataStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize) { } public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) { Stream stream = null; ContentDispositionHeaderValue contentDisposition = headers.ContentDisposition; if (contentDisposition != null) { if (!String.IsNullOrEmpty(contentDisposition.FileName)) { FileData.Add(new MultipartFileData(headers, contentDisposition.FileName)); // Can be replaced with any stream the user want. e.g. Azure Blob Storage Stream. stream = new MemoryStream(); } else { stream = base.GetStream(parent, headers); } } return stream; } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartMemoryStreamProviderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class MultipartMemoryStreamProviderTests : MultipartStreamProviderTestBase { [Fact] public void GetStream_ReturnsNewMemoryStream() { // Arrange MultipartMemoryStreamProvider instance = new MultipartMemoryStreamProvider(); HttpContent parent = new StringContent(String.Empty); HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act Stream stream1 = instance.GetStream(parent, headers); Stream stream2 = instance.GetStream(parent, headers); // Assert Assert.IsType(stream1); Assert.Equal(0, stream1.Length); Assert.Equal(0, stream1.Position); Assert.IsType(stream2); Assert.Equal(0, stream2.Length); Assert.Equal(0, stream2.Position); Assert.NotSame(stream1, stream2); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartRelatedStreamProviderTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http { public class MultipartRelatedStreamProviderTests : MultipartStreamProviderTestBase { private const string ContentID = "12345"; private const string Boundary = "-A-"; private const string DefaultRootContent = "Default root content"; private const string ContentIDRootContent = "Content with matching Content-ID"; private const string OtherContent = "Other Content"; public static TheoryDataSet MultipartRelatedWithStartParameter { get { return new TheoryDataSet { { String.Format("multipart/related; boundary={0}; start=\"{1}\"", Boundary, ContentID) }, { String.Format("multipart/related; start={0}; boundary={1}", ContentID, Boundary) }, }; } } public static TheoryDataSet MultipartWithMissingOrInvalidStartParameter { get { return new TheoryDataSet { { String.Format("multipart/form-data; start=\"{0}\"; boundary={1}", ContentID, Boundary) }, { String.Format("multipart/form-data; start={0}; boundary={1}", ContentID, Boundary) }, { String.Format("multipart/form-data; boundary={0}", Boundary) }, { String.Format("multipart/related; boundary={0}", Boundary) }, { String.Format("multipart/mixed; start={0}; boundary={1}", ContentID, Boundary) }, { String.Format("multipart/mixed; boundary={1}", ContentID, Boundary) }, }; } } [Fact] public void RootContent_ReturnsNull() { MultipartRelatedStreamProvider provider = new MultipartRelatedStreamProvider(); Assert.Null(provider.RootContent); } [Theory] [PropertyData("MultipartRelatedWithStartParameter")] public async Task RootContent_ReturnsNullIfContentIDIsNotMatched(string mediaType) { // Arrange MultipartContent content = new MultipartContent("related", Boundary); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); content.Add(new StringContent(DefaultRootContent)); content.Add(new StringContent(OtherContent)); HttpContent expectedRootContent = new StringContent(ContentIDRootContent); expectedRootContent.Headers.Add("Content-ID", "NoMatch"); content.Add(expectedRootContent); MultipartRelatedStreamProvider provider = await content.ReadAsMultipartAsync(new MultipartRelatedStreamProvider()); // Act HttpContent actualRootContent = provider.RootContent; // Assert Assert.Null(actualRootContent); } [Theory] [PropertyData("MultipartRelatedWithStartParameter")] public async Task RootContent_PicksContent_WithStartParameter(string mediaType) { var result = await RootContent_PicksContent_Setup(mediaType); Assert.Equal(ContentIDRootContent, result); } [Theory] [PropertyData("MultipartWithMissingOrInvalidStartParameter")] public async Task RootContent_PicksContent_WithoutStartParameter(string mediaType) { var result = await RootContent_PicksContent_Setup(mediaType); Assert.Equal(DefaultRootContent, result); } private async Task RootContent_PicksContent_Setup(string mediaType) { // Arrange MultipartContent content = new MultipartContent("related", Boundary); content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType); content.Add(new StringContent(DefaultRootContent)); content.Add(new StringContent(OtherContent)); HttpContent contentIDContent = new StringContent(ContentIDRootContent); contentIDContent.Headers.Add("Content-ID", ContentID); content.Add(contentIDContent); MultipartRelatedStreamProvider provider = await content.ReadAsMultipartAsync(new MultipartRelatedStreamProvider()); // Act HttpContent actualRootContent = provider.RootContent; return await actualRootContent.ReadAsStringAsync(); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartRemoteFileDataTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class MultipartRemoteFileDataTests { [Fact] public void Constructor_ThrowsOnNullHeaders() { // Arrange, Act & Assert Assert.ThrowsArgumentNull( () => new MultipartRemoteFileData(null, "http://some/path/to", "Name"), "headers"); } [Fact] public void Constructor_ThrowsOnNullLocation() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act and Assert Assert.ThrowsArgumentNull(() => new MultipartRemoteFileData(headers, null, "Name"), "location"); } [Fact] public void Constructor_ThrowsOnNullFileName() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act and Assert Assert.ThrowsArgumentNull(() => new MultipartRemoteFileData(headers, "http://some/path/to", null), "fileName"); } [Fact] public void Constructor_InitializesCorrectly() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); string remoteFileURL = "http://some/path/to"; string fileName = "Name"; // Act MultipartRemoteFileData fileData = new MultipartRemoteFileData(headers, remoteFileURL, fileName); // Assert Assert.Same(headers, fileData.Headers); Assert.Same(remoteFileURL, fileData.Location); Assert.Same(fileName, fileData.FileName); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/MultipartStreamProviderTestBase.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.ObjectModel; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; namespace System.Net.Http { public abstract class MultipartStreamProviderTestBase where TProvider : MultipartStreamProvider, new() { protected MultipartStreamProviderTestBase() { } [Fact] public void Contents_IsEmpty() { // Arrange TProvider provider = new TProvider(); // Act Collection contents = provider.Contents; // Assert Assert.Empty(contents); } [Fact] public void PostProcessing_ReturnsCompleteTask() { // Arrange TProvider provider = new TProvider(); // Act Task postProcessing = provider.ExecutePostProcessingAsync(); // Assert Assert.Equal(TaskStatus.RanToCompletion, postProcessing.Status); } [Fact] public void GetStream_ThrowsOnNullParent() { TProvider provider = new TProvider(); HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); Assert.ThrowsArgumentNull(() => provider.GetStream(null, headers), "parent"); } [Fact] public void GetStream_ThrowsOnNullHeaders() { TProvider provider = new TProvider(); StringContent content = new StringContent(String.Empty); Assert.ThrowsArgumentNull(() => provider.GetStream(content, null), "headers"); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/ObjectContentOfTTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Net.Http.Formatting; using System.Net.Http.Headers; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class ObjectContentOfTTests { private MediaTypeHeaderValue _jsonHeaderValue = new MediaTypeHeaderValue("application/json"); [Fact] public void Constructor_WhenFormatterParameterIsNull_Throws() { Assert.ThrowsArgumentNull(() => new ObjectContent("", formatter: null), "formatter"); Assert.ThrowsArgumentNull(() => new ObjectContent("", formatter: null, mediaType: "foo/bar"), "formatter"); Assert.ThrowsArgumentNull(() => new ObjectContent("", formatter: null, mediaType: _jsonHeaderValue), "formatter"); } [Fact] public void Constructor_SetsFormatterProperty() { var formatterMock = new Mock(); formatterMock.Setup(f => f.CanWriteType(typeof(String))).Returns(true); var formatter = formatterMock.Object; var content = new ObjectContent(null, formatter, mediaType: (MediaTypeHeaderValue)null); Assert.Same(formatter, content.Formatter); } [Fact] public void Constructor_CallsFormattersGetDefaultContentHeadersMethod() { var formatterMock = new Mock(); formatterMock.Setup(f => f.CanWriteType(typeof(String))).Returns(true); var content = new ObjectContent(typeof(string), "", formatterMock.Object, _jsonHeaderValue); formatterMock.Verify(f => f.SetDefaultContentHeaders(typeof(string), content.Headers, _jsonHeaderValue), Times.Once()); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/ObjectContentTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; namespace System.Net.Http { public class ObjectContentTests { private readonly object _value = new object(); private readonly MediaTypeFormatter _formatter = new TestableMediaTypeFormatter(); private readonly MediaTypeHeaderValue _jsonHeaderValue = new MediaTypeHeaderValue("application/json"); [Fact] public void Constructor_WhenTypeArgumentIsNull_ThrowsEsxception() { Assert.ThrowsArgumentNull(() => new ObjectContent(null, _value, _formatter), "type"); Assert.ThrowsArgumentNull(() => new ObjectContent(null, _value, _formatter, mediaType: "foo/bar"), "type"); Assert.ThrowsArgumentNull(() => new ObjectContent(null, _value, _formatter, mediaType: _jsonHeaderValue), "type"); } [Fact] public void Constructor_WhenFormatterArgumentIsNull_ThrowsEsxception() { Assert.ThrowsArgumentNull(() => new ObjectContent(typeof(Object), _value, formatter: null), "formatter"); Assert.ThrowsArgumentNull(() => new ObjectContent(typeof(Object), _value, formatter: null, mediaType: "foo/bar"), "formatter"); Assert.ThrowsArgumentNull(() => new ObjectContent(typeof(Object), _value, formatter: null, mediaType: _jsonHeaderValue), "formatter"); } [Fact] public void Constructor_WhenValueIsNullAndTypeIsNotCompatible_ThrowsException() { Assert.Throws(() => { new ObjectContent(typeof(int), null, new JsonMediaTypeFormatter()); }, "The 'ObjectContent' type cannot accept a null value for the value type 'Int32'."); } [Fact] public void Constructor_WhenValueIsNotNullButTypeDoesNotMatch_ThrowsException() { Assert.ThrowsArgument(() => { new ObjectContent(typeof(IList), new Dictionary(), new JsonMediaTypeFormatter()); }, "value", "An object of type 'Dictionary`2' cannot be used with a type parameter of 'IList`1'."); } [Fact] public void Constructor_WhenValueIsNotSupportedByFormatter_ThrowsException() { Mock formatterMock = new Mock(); formatterMock.Setup(f => f.CanWriteType(typeof(List))).Returns(false).Verifiable(); Assert.Throws(() => { new ObjectContent(typeof(List), new List(), formatterMock.Object); }, "The configured formatter 'Castle.Proxies.MediaTypeFormatterProxy' cannot write an object of type 'List`1'."); formatterMock.Verify(); } [Fact] public void Constructor_SetsFormatterProperty() { var content = new ObjectContent(typeof(object), _value, _formatter, mediaType: (MediaTypeHeaderValue)null); Assert.Same(_formatter, content.Formatter); } [Fact] public void Constructor_CallsFormattersGetDefaultContentHeadersMethod() { var formatterMock = new Mock(); formatterMock.Setup(f => f.CanWriteType(typeof(String))).Returns(true); var content = new ObjectContent(typeof(string), "", formatterMock.Object, _jsonHeaderValue); formatterMock.Verify(f => f.SetDefaultContentHeaders(typeof(string), content.Headers, _jsonHeaderValue), Times.Once()); } [Theory] [PropertyData("ValidValueTypePairs")] public void Constructor_WhenValueAndTypeAreCompatible_SetsValue(Type type, object value) { var oc = new ObjectContent(type, value, new JsonMediaTypeFormatter()); Assert.Same(value, oc.Value); Assert.Equal(type, oc.ObjectType); } [Fact] public void Constructor_WhenTypeIsNotSupportedByFormatter_ThrowsException() { Mock formatterMock = new Mock(); formatterMock.Setup(f => f.CanWriteType(typeof(string))).Returns(true); formatterMock.Setup(f => f.CanWriteType(typeof(object))).Returns(false).Verifiable(); Assert.Throws(() => { var content = new ObjectContent(typeof(object), "", formatterMock.Object); }, "The configured formatter 'Castle.Proxies.MediaTypeFormatterProxy' cannot write an object of type 'Object'."); formatterMock.Verify(); } public static TheoryDataSet ValidValueTypePairs { get { return new TheoryDataSet { { typeof(Nullable), null }, { typeof(string), null }, { typeof(int), 42 }, //{ typeof(int), (short)42 }, TODO should this work? { typeof(object), "abc" }, { typeof(string), "abc" }, { typeof(IList), new List() }, }; } } [Fact] public void SerializeToStreamAsync_CallsUnderlyingFormatter() { var stream = Stream.Null; var context = new Mock().Object; var formatterMock = new Mock { CallBase = true }; var oc = new TestableObjectContent(typeof(string), "abc", formatterMock.Object); var task = new Task(() => { }); formatterMock.Setup(f => f.WriteToStreamAsync(typeof(string), "abc", stream, oc, context)) .Returns(task).Verifiable(); var result = oc.CallSerializeToStreamAsync(stream, context); Assert.Same(task, result); formatterMock.VerifyAll(); } [Fact] public void TryComputeLength_ReturnsFalseAnd0() { var oc = new TestableObjectContent(typeof(string), null, _formatter); long length; var result = oc.CallTryComputeLength(out length); Assert.False(result); Assert.Equal(-1, length); } public class TestableObjectContent : ObjectContent { public TestableObjectContent(Type type, object value, MediaTypeFormatter formatter) : base(type, value, formatter) { } public bool CallTryComputeLength(out long length) { return TryComputeLength(out length); } public Task CallSerializeToStreamAsync(Stream stream, TransportContext context) { return SerializeToStreamAsync(stream, context); } } public class TestableMediaTypeFormatter : MediaTypeFormatter { public TestableMediaTypeFormatter() { SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); } public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext) { throw new NotImplementedException(); } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/ParserData.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { internal static class ParserData { public const int MinHeaderSize = 2; public const int MinMessageSize = 10; public const int MinRequestLineSize = 14; public const int MinStatusLineSize = 15; public const int MinBufferSize = 256; public static TheoryDataSet Versions { get { return new TheoryDataSet { Version.Parse("1.0"), Version.Parse("1.1"), Version.Parse("1.2"), Version.Parse("2.0"), Version.Parse("10.0"), Version.Parse("1.15"), }; } } public static TheoryDataSet InvalidVersions { get { return new TheoryDataSet { "", "http/1.1", "HTTP/a.1", "HTTP/1.a", "HTTP 1.1", "HTTP\t1.1", "HTTP 1 1", "\0", "HTTP\01.1", "HTTP/4294967295.4294967295", "æææøøøååå", "HTTP/æææøøøååå", "いくつかのテキスト", "HTTP/いくつかのテキスト", }; } } public static TheoryDataSet InvalidMethods { get { return new TheoryDataSet { "", "G\tT", "G E T", "\0", "G\0T", "GET\n", "æææøøøååå", "いくつかのテキスト", }; } } public static TheoryDataSet ValidReasonPhrases { get { return new TheoryDataSet { "", "Ok", "public Server Error", "r e a s o n", "reason ", " reason ", }; } } public static TheoryDataSet InvalidReasonPhrases { get { return new TheoryDataSet { "\0", "\t", "reason\n", "æææøøøååå", "いくつかのテキスト", }; } } // This deliberately only checks for syntactic boundaries of the URI, not its content public static TheoryDataSet InvalidRequestUris { get { return new TheoryDataSet { "", "p a t h", "path ", " path ", "æææø ø øååå", "いくつか の テキスト" }; } } public static TheoryDataSet InvalidStatusCodes { get { return new TheoryDataSet { "0", "99", "1a1", "abc", "1001", "2000", Int32.MinValue.ToString(), Int32.MaxValue.ToString(), "æææøøøååå", "いくつかのテキスト" }; } } public static readonly Dictionary ValidHeaders = new Dictionary { { "N0", "V0"}, { "N1", "V1"}, { "N2", "V2"}, { "N3", "V3"}, { "N4", "V4"}, { "N5", "V5"}, { "N6", "V6"}, { "N7", "V7"}, { "N8", "V8"}, { "N9", "V9"}, }; public static readonly string HttpMethod = "TEG"; public static readonly HttpStatusCode HttpStatus = HttpStatusCode.Created; public static readonly string HttpReasonPhrase = "ReasonPhrase"; public static readonly string HttpHostName = "example.com"; public static readonly int HttpHostPort = 1234; public static readonly string HttpMessageEntity = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; public static readonly Uri HttpRequestUri = new Uri("http://" + HttpHostName + "/some/path"); public static readonly Uri HttpRequestUriWithPortAndQuery = new Uri("http://" + HttpHostName + ":" + HttpHostPort + "/some/path?%C3%A6%C3%B8%C3%A5"); public static readonly Uri HttpsRequestUri = new Uri("https://" + HttpHostName + "/some/path"); public static readonly string TextContentType = "text/plain; charset=utf-8"; public static readonly MediaTypeHeaderValue HttpRequestMediaType = MediaTypeHeaderValue.Parse("application/http; msgtype=request"); public static readonly MediaTypeHeaderValue HttpResponseMediaType = MediaTypeHeaderValue.Parse("application/http; msgtype=response"); public static readonly string HttpRequest = HttpMethod + " /some/path HTTP/1.2\r\nHost: " + HttpHostName + "\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\n\r\n"; public static readonly string HttpRequestWithHost = HttpMethod + " /some/path HTTP/1.2\r\n" + "N1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nHost: " + HttpHostName + "\r\n\r\n"; public static readonly string HttpRequestWithPortAndQuery = HttpMethod + " /some/path?%C3%A6%C3%B8%C3%A5 HTTP/1.2\r\nHost: " + HttpHostName + ":" + HttpHostPort.ToString() + "\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\n\r\n"; public static readonly string HttpResponse = "HTTP/1.2 " + ((int)HttpStatus).ToString() + " " + HttpReasonPhrase + "\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Length: 0\r\n\r\n"; public static readonly string HttpRequestWithEntity = HttpMethod + " /some/path HTTP/1.2\r\nHost: " + HttpHostName + "\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Type: " + TextContentType + "\r\nContent-Length: 100" + "\r\n\r\n" + HttpMessageEntity; public static readonly string HttpResponseWithEntity = "HTTP/1.2 " + ((int)HttpStatus).ToString() + " " + HttpReasonPhrase + "\r\nN1: V1a, V1b, V1c, V1d, V1e\r\nN2: V2\r\nContent-Type: " + TextContentType + "\r\nContent-Length: 100" + "\r\n\r\n" + HttpMessageEntity; } } ================================================ FILE: test/System.Net.Http.Formatting.Test/PushStreamContentTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Formatting; using System.Threading; using System.Threading.Tasks; using Microsoft.TestCommon; using Moq; using Moq.Protected; namespace System.Net.Http { public class PushStreamContentTest { [Fact] public void Constructor_ThrowsOnNullAction() { Action action = null; Assert.ThrowsArgumentNull(() => new PushStreamContent(action), "onStreamAvailable"); } [Fact] public void Constructor_SetsDefaultMediaType() { Action streamAction = new MockStreamAction().Action; PushStreamContent content = new PushStreamContent(streamAction); Assert.Equal(MediaTypeConstants.ApplicationOctetStreamMediaType, content.Headers.ContentType); } [Fact] public void Constructor_SetsMediaTypeFromString() { Action streamAction = new MockStreamAction().Action; PushStreamContent content = new PushStreamContent(streamAction, "text/xml"); Assert.Equal(MediaTypeConstants.TextXmlMediaType, content.Headers.ContentType); } [Fact] public void Constructor_SetsMediaType() { Action streamAction = new MockStreamAction().Action; PushStreamContent content = new PushStreamContent(streamAction, MediaTypeConstants.TextXmlMediaType); Assert.Equal(MediaTypeConstants.TextXmlMediaType, content.Headers.ContentType); } [Fact] public async Task SerializeToStreamAsync_CallsAction() { // Arrange MemoryStream outputStream = new MemoryStream(); MockStreamAction streamAction = new MockStreamAction(close: true); PushStreamContent content = new PushStreamContent((Action)streamAction.Action); // Act await content.CopyToAsync(outputStream); // Assert Assert.True(streamAction.WasInvoked); Assert.Same(content, streamAction.Content); Assert.IsType(streamAction.OutputStream); // We don't close the underlying stream Assert.True(outputStream.CanRead); } [Fact] public async Task SerializeToStreamAsync_CompletesTaskOnActionException() { // Arrange MemoryStream outputStream = new MemoryStream(); MockStreamAction streamAction = new MockStreamAction(throwException: true); PushStreamContent content = new PushStreamContent((Action)streamAction.Action); // Act & Assert await Assert.ThrowsAsync(() => content.CopyToAsync(outputStream)); Assert.True(streamAction.WasInvoked); Assert.IsType(streamAction.OutputStream); Assert.True(outputStream.CanRead); } #if Testing_NetStandard1_3 [Fact] public async Task CompleteTaskOnCloseStream_Dispose_CompletesTaskButDoNotDisposeInnerStream() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; TaskCompletionSource serializeToStreamTask = new TaskCompletionSource(); MockCompleteTaskOnCloseStream mockStream = new MockCompleteTaskOnCloseStream(mockInnerStream.Object, serializeToStreamTask); // Act mockStream.Dispose(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Never(), exactParameterMatch: true, args: true); Assert.Equal(TaskStatus.RanToCompletion, serializeToStreamTask.Task.Status); Assert.True(await serializeToStreamTask.Task); } #else [Fact] public async Task CompleteTaskOnCloseStream_Dispose_CompletesTaskButDoNotCloseInnerStream() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; TaskCompletionSource serializeToStreamTask = new TaskCompletionSource(); MockCompleteTaskOnCloseStream mockStream = new MockCompleteTaskOnCloseStream(mockInnerStream.Object, serializeToStreamTask); // Act mockStream.Dispose(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Never(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Never()); Assert.Equal(TaskStatus.RanToCompletion, serializeToStreamTask.Task.Status); Assert.True(await serializeToStreamTask.Task); } [Fact] public async Task NonClosingDelegatingStream_Close_CompletesTaskButDoNotCloseInnerStream() { // Arrange Mock mockInnerStream = new Mock() { CallBase = true }; TaskCompletionSource serializeToStreamTask = new TaskCompletionSource(); MockCompleteTaskOnCloseStream mockStream = new MockCompleteTaskOnCloseStream(mockInnerStream.Object, serializeToStreamTask); // Act mockStream.Close(); // Assert mockInnerStream.Protected().Verify("Dispose", Times.Never(), exactParameterMatch: true, args: true); mockInnerStream.Verify(s => s.Close(), Times.Never()); Assert.Equal(TaskStatus.RanToCompletion, serializeToStreamTask.Task.Status); Assert.True(await serializeToStreamTask.Task); } #endif [Fact] public async Task PushStreamContentWithAsyncOnStreamAvailableHandler_ExceptionsInOnStreamAvailable_AreCaught() { // Arrange bool faulted = false; Exception exception = new ApplicationException(); PushStreamContent content = new PushStreamContent(async (s, c, tc) => { await Task.FromResult(42); throw exception; }); MemoryStream stream = new MemoryStream(); try { // Act await content.CopyToAsync(stream); } catch (ApplicationException e) { Assert.Same(exception, e); faulted = true; } // Assert Assert.True(faulted); } [Fact] public async Task PushStream_HttpContentIntegrationTest() { // Arrange var expected = "Hello, world!"; using (var client = new MockHttpClient()) { // We mock the client, so this doesn't actually hit the web. This client will just echo back // the body content we give it. using (var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:30000/")) { request.Content = new PushStreamContent((stream, content, context) => { using (var writer = new StreamWriter(stream)) { writer.Write(expected); } }, "text/plain"); // Act using (var response = await client.SendAsync(request, CancellationToken.None)) { // Assert response.EnsureSuccessStatusCode(); var responseText = await response.Content.ReadAsStringAsync(); Assert.Equal(expected, responseText); } } } } private class MockStreamAction { bool _close; bool _throwException; public MockStreamAction(bool close = false, bool throwException = false) { _close = close; _throwException = throwException; } public bool WasInvoked { get; private set; } public Stream OutputStream { get; private set; } public HttpContent Content { get; private set; } public TransportContext TransportContext { get; private set; } public void Action(Stream stream, HttpContent content, TransportContext context) { WasInvoked = true; OutputStream = stream; Content = content; TransportContext = context; if (_close) { #if Testing_NetStandard1_3 stream.Dispose(); #else stream.Close(); #endif } if (_throwException) { throw new ApplicationException("Action threw exception!"); } } } internal class MockCompleteTaskOnCloseStream : PushStreamContent.CompleteTaskOnCloseStream { public MockCompleteTaskOnCloseStream(Stream innerStream, TaskCompletionSource serializeToStreamTask) : base(innerStream, serializeToStreamTask) { } } private class MockHttpClient : HttpClient { public override async Task SendAsync(HttpRequestMessage request, Threading.CancellationToken cancellationToken) { var stream = new MemoryStream(); await request.Content.CopyToAsync(stream); stream.Position = 0; return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(stream), }; } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/RemoteStreamInfoTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Net.Http.Headers; using Microsoft.TestCommon; namespace System.Net.Http { public class RemoteStreamInfoTests { [Fact] public void Constructor_ThrowsOnNullStream() { // Arrange, Act & Assert Assert.ThrowsArgumentNull( () => new RemoteStreamInfo(null, "http://some/path/to", "Name"), "remoteStream"); } [Fact] public void Constructor_ThrowsOnNullLocation() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act & Assert Assert.ThrowsArgumentNull(() => new RemoteStreamInfo(new MemoryStream(), null, "Name"), "location"); } [Fact] public void Constructor_ThrowsOnNullFileName() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); // Act & Assert Assert.ThrowsArgumentNull(() => new RemoteStreamInfo(new MemoryStream(), "http://some/path/to", null), "fileName"); } [Fact] public void Constructor_InitializesCorrectly() { // Arrange HttpContentHeaders headers = FormattingUtilities.CreateEmptyContentHeaders(); string remoteFileURL = "http://some/path/to"; string fileName = "Name"; Stream stream = new MemoryStream(); // Act RemoteStreamInfo fileData = new RemoteStreamInfo(stream, remoteFileURL, fileName); // Assert Assert.Same(stream, fileData.RemoteStream); Assert.Same(remoteFileURL, fileData.Location); Assert.Same(fileName, fileData.FileName); } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/System.Net.Http.Formatting.Test.csproj ================================================  {7AF77741-9158-4D5F-8782-8F21FADF025F} Library Properties System.Net.Http System.Net.Http.Formatting.Test ..\..\bin\$(Configuration)\Test\ true ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll False True ..\..\packages\System.IO.Pipelines.4.7.5\lib\net461\System.IO.Pipelines.dll False True ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll False True ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll False True ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll False True ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll True ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll True ..\..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll True ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True False {668E9021-CE84-49D9-98FB-DF125A9FCDB0} System.Net.Http.Formatting {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} Microsoft.TestCommon Designer This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/System.Net.Http.Formatting.Test/UriExtensionsTests.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Specialized; using System.Net.Http.Formatting.DataSets; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; namespace System.Net.Http { public class UriExtensionsTests { private static readonly Uri TestAddress = new Uri("http://www.example.com"); private static readonly Type TestType = typeof(string); [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(UriExtensions), TypeAssert.TypeProperties.IsPublicVisibleClass | TypeAssert.TypeProperties.IsStatic); } [Fact] public void ParseQueryStringThrowsWithNull() { Assert.ThrowsArgumentNull(() => ((Uri)null).ParseQueryString(), "address"); } [Theory] [TestDataSet(typeof(HttpTestData), "UriTestData")] public void ParseQueryStringSucceeds(Uri address) { var result = address.ParseQueryString(); Assert.NotNull(result); bool addressContainsQuery = address.Query.Contains("?"); if (!addressContainsQuery) { Assert.Empty(result); } else { Assert.True(result.Count > 0, "Uri with query string should return non-empty set."); } } [Fact] public void TryReadQueryAsJsonThrowsWithNull() { JObject value; Assert.ThrowsArgumentNull(() => ((Uri)null).TryReadQueryAsJson(out value), "address"); } [Theory] [TestDataSet(typeof(HttpTestData), "UriTestData")] public void TryReadQueryAsJsonSucceeds(Uri address) { JObject value; Assert.True(address.TryReadQueryAsJson(out value), "Expected 'true' as result"); Assert.NotNull(value); Assert.IsType(value); } [Fact] public void TryReadQueryAsThrowsWithNull() { object value; Assert.ThrowsArgumentNull(() => ((Uri)null).TryReadQueryAs(TestType, out value), "address"); Assert.ThrowsArgumentNull(() => TestAddress.TryReadQueryAs(null, out value), "type"); } [Fact] public void TryReadQueryAsSucceeds() { object value; UriBuilder address = new UriBuilder("http://some.host"); address.Query = "a=2"; Assert.True(address.Uri.TryReadQueryAs(typeof(SimpleObject1), out value), "Expected 'true' reading valid data"); SimpleObject1 so1 = (SimpleObject1)value; Assert.NotNull(so1); Assert.Equal(2, so1.a); address.Query = "b=true"; Assert.True(address.Uri.TryReadQueryAs(typeof(SimpleObject2), out value), "Expected 'true' reading valid data"); SimpleObject2 so2 = (SimpleObject2)value; Assert.NotNull(so2); Assert.True(so2.b, "Value should have been true"); address.Query = "c=hello"; Assert.True(address.Uri.TryReadQueryAs(typeof(SimpleObject3), out value), "Expected 'true' reading valid data"); SimpleObject3 so3 = (SimpleObject3)value; Assert.NotNull(so3); Assert.Equal("hello", so3.c); address.Query = "c="; Assert.True(address.Uri.TryReadQueryAs(typeof(SimpleObject3), out value), "Expected 'true' reading valid data"); so3 = (SimpleObject3)value; Assert.NotNull(so3); Assert.Equal("", so3.c); address.Query = "c=null"; Assert.True(address.Uri.TryReadQueryAs(typeof(SimpleObject3), out value), "Expected 'true' reading valid data"); so3 = (SimpleObject3)value; Assert.NotNull(so3); Assert.Equal("null", so3.c); } [Fact] public void TryReadQueryAsTThrowsWithNull() { object value; Assert.ThrowsArgumentNull(() => ((Uri)null).TryReadQueryAs(out value), "address"); } [Fact] public void TryReadQueryAsTSucceeds() { UriBuilder address = new UriBuilder("http://some.host"); address.Query = "a=2"; SimpleObject1 so1; Assert.True(address.Uri.TryReadQueryAs(out so1), "Expected 'true' reading valid data"); Assert.NotNull(so1); Assert.Equal(2, so1.a); address.Query = "b=true"; SimpleObject2 so2; Assert.True(address.Uri.TryReadQueryAs(out so2), "Expected 'true' reading valid data"); Assert.NotNull(so2); Assert.True(so2.b, "Value should have been true"); address.Query = "c=hello"; SimpleObject3 so3; Assert.True(address.Uri.TryReadQueryAs(out so3), "Expected 'true' reading valid data"); Assert.NotNull(so3); Assert.Equal("hello", so3.c); address.Query = "c="; Assert.True(address.Uri.TryReadQueryAs(out so3), "Expected 'true' reading valid data"); Assert.NotNull(so3); Assert.Equal("", so3.c); address.Query = "c=null"; Assert.True(address.Uri.TryReadQueryAs(out so3), "Expected 'true' reading valid data"); Assert.NotNull(so3); Assert.Equal("null", so3.c); } public class SimpleObject1 { public int a { get; set; } } public class SimpleObject2 { public bool b { get; set; } } public class SimpleObject3 { public string c { get; set; } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/UriQueryDataSet.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Net.Http { public class UriQueryTestData { public static TheoryDataSet UriQueryData { get { return new TheoryDataSet { { "=", "", "" }, { "N=", "N", "" }, { "=N", "", "N" }, { "N=V", "N", "V" }, { "%26=%26", "&", "&" }, { "%3D=%3D", "=", "=" }, { "N=A%2BC", "N", "A+C" }, { "N=100%25AA%21", "N", "100%AA!"}, { "N=%7E%21%40%23%24%25%5E%26%2A%28%29_%2B","N","~!@#$%^&*()_+"}, { "N=%601234567890-%3D", "N", "`1234567890-="}, { "N=%60%31%32%33%34%35%36%37%38%39%30%2D%3D","N", "`1234567890-="}, { "N=%E6%BF%80%E5%85%89%E9%80%99%E5%85%A9%E5%80%8B%E5%AD%97%E6%98%AF%E7%94%9A%E9%BA%BC%E6%84%8F%E6%80%9D", "N", "激光這兩個字是甚麼意思" }, { "N=%C3%A6%C3%B8%C3%A5","N","æøå"}, { "N=%C3%A6+%C3%B8+%C3%A5","N","æ ø å"}, }; } } } } ================================================ FILE: test/System.Net.Http.Formatting.Test/packages.config ================================================  ================================================ FILE: test/System.Net.Http.Formatting.ns1_3.Test/System.Net.Http.Formatting.ns1_3.Test.csproj ================================================  net462;netcoreapp2.1;net8.0 System.Net.Http ..\..\bin\$(Configuration)\Test\ns1_3\ $(Configurations);CodeAnalysis false $(DefineConstants);Testing_NetStandard1_3 true true <_VSRunnerVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">2.4.3 <_VSRunnerVersion Condition=" '$(TargetFramework)' != 'netcoreapp2.1' ">2.4.5 all runtime; build; native; contentfiles; analyzers %(RecursiveDir)\%(Filename).cs ================================================ FILE: test/System.Net.Http.Formatting.ns2_0.Test/System.Net.Http.Formatting.ns2_0.Test.csproj ================================================  net462;netcoreapp2.1;net8.0 System.Net.Http ..\..\bin\$(Configuration)\Test\ns2_0\ $(Configurations);CodeAnalysis false $(DefineConstants);Testing_NetStandard2_0 true true <_VSRunnerVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">2.4.3 <_VSRunnerVersion Condition=" '$(TargetFramework)' != 'netcoreapp2.1' ">2.4.5 all runtime; build; native; contentfiles; analyzers %(RecursiveDir)\%(Filename).cs ================================================ FILE: test/System.Web.Cors.Test/CorsEngineTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using Microsoft.TestCommon; namespace System.Web.Cors.Test { public class CorsEngineTest { [Fact] public void EvaluatePolicy_NullRequest_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.EvaluatePolicy(null, new CorsPolicy()), "requestContext"); } [Fact] public void EvaluatePolicy_NullPolicy_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.EvaluatePolicy(new CorsRequestContext(), null), "policy"); } [Fact] public void EvaluatePolicy_NoOrigin_ReturnsInvalidResult() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = null, HttpMethod = "GET" }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, new CorsPolicy()); Assert.False(result.IsValid); Assert.Contains("The request does not contain the Origin header.", result.ErrorMessages); } [Fact] public void EvaluatePolicy_NoMatchingOrigin_ReturnsInvalidResult() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy(); policy.Origins.Add("bar"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.False(result.IsValid); Assert.Contains("The origin 'foo' is not allowed.", result.ErrorMessages); } [Fact] public void EvaluatePolicy_EmptyOriginsPolicy_ReturnsInvalidResult() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy(); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.False(result.IsValid); Assert.Contains("The origin 'foo' is not allowed.", result.ErrorMessages); } [Fact] public void EvaluatePolicy_AllowAnyOrigin_DoesNotSupportCredentials_EmitsWildcardForOrigin() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, SupportsCredentials = false }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.Equal("*", result.AllowedOrigin); } [Fact] public void EvaluatePolicy_AllowAnyOrigin_SupportsCredentials_AddsSpecificOrigin() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, SupportsCredentials = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.Equal("foo", result.AllowedOrigin); } [Fact] public void EvaluatePolicy_DoesNotSupportCredentials_AllowCredentialsReturnsFalse() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, SupportsCredentials = false }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.False(result.SupportsCredentials); } [Fact] public void EvaluatePolicy_SupportsCredentials_AllowCredentialsReturnsTrue() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, SupportsCredentials = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(result.SupportsCredentials); } [Fact] public void EvaluatePolicy_NoExposedHeaders_NoAllowExposedHeaders() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.Empty(result.AllowedExposedHeaders); } [Fact] public void EvaluatePolicy_OneExposedHeaders_HeadersAllowed() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; policy.ExposedHeaders.Add("foo"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.Equal(1, result.AllowedExposedHeaders.Count); Assert.Contains("foo", result.AllowedExposedHeaders); } [Fact] public void EvaluatePolicy_ManyExposedHeaders_HeadersAllowed() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; policy.ExposedHeaders.Add("foo"); policy.ExposedHeaders.Add("bar"); policy.ExposedHeaders.Add("baz"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.Equal(3, result.AllowedExposedHeaders.Count()); Assert.Contains("foo", result.AllowedExposedHeaders); Assert.Contains("bar", result.AllowedExposedHeaders); Assert.Contains("baz", result.AllowedExposedHeaders); } [Fact] public void EvaluatePolicy_PreflightRequest_MethodNotAllowed_ReturnsInvalidResult() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; policy.Methods.Add("GET"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.False(result.IsValid); Assert.Contains("The method 'PUT' is not allowed.", result.ErrorMessages); } [Fact] public void EvaluatePolicy_PreflightRequest_MethodAllowed_ReturnsAllowMethods() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; policy.Methods.Add("PUT"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.NotNull(result); Assert.Contains("PUT", result.AllowedMethods); } [Fact] public void EvaluatePolicy_PreflightRequest_OriginAllowed_ReturnsOrigin() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyMethod = true }; policy.Origins.Add("foo"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Equal("foo", result.AllowedOrigin); } [Fact] public void EvaluatePolicy_PreflightRequest_SupportsCredentials_AllowCredentialsReturnsTrue() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo", }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true, SupportsCredentials = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.True(result.SupportsCredentials); } [Fact] public void EvaluatePolicy_PreflightRequest_NoPreflightMaxAge_NoPreflightMaxAgeSet() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo", }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true, PreflightMaxAge = null }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Null(result.PreflightMaxAge); } [Fact] public void EvaluatePolicy_PreflightRequest_PreflightMaxAge_PreflightMaxAgeSet() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo", }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true, PreflightMaxAge = 10 }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Equal(10, result.PreflightMaxAge); } [Fact] public void EvaluatePolicy_PreflightRequest_AnyMethod_ReturnsRequestMethod() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "GET", Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Equal(1, result.AllowedMethods.Count); Assert.Contains("GET", result.AllowedMethods); } [Fact] public void EvaluatePolicy_PreflightRequest_ListedMethod_ReturnsSubsetOfListedMethods() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true }; policy.Methods.Add("PUT"); policy.Methods.Add("DELETE"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Single(result.AllowedMethods); Assert.Contains("PUT", result.AllowedMethods); } [Fact] public void EvaluatePolicy_PreflightRequest_NoHeadersRequested_AllowedAllHeaders_ReturnsEmptyHeaders() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo", }; CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true, AllowAnyHeader = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Empty(result.AllowedHeaders); } [Fact] public void EvaluatePolicy_PreflightRequest_HeadersRequested_AllowAllHeaders_ReturnsRequestedHeaders() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; requestContext.AccessControlRequestHeaders.Add("foo"); requestContext.AccessControlRequestHeaders.Add("bar"); CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyHeader = true, AllowAnyMethod = true }; CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Equal(2, result.AllowedHeaders.Count()); Assert.Contains("foo", result.AllowedHeaders); Assert.Contains("bar", result.AllowedHeaders); } [Fact] public void EvaluatePolicy_PreflightRequest_HeadersRequested_AllowSomeHeaders_ReturnsSubsetOfListedHeaders() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; requestContext.AccessControlRequestHeaders.Add("Content-Type"); CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true }; policy.Headers.Add("foo"); policy.Headers.Add("bar"); policy.Headers.Add("Content-Type"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.Equal(1, result.AllowedHeaders.Count); Assert.Contains("Content-Type", result.AllowedHeaders); } [Fact] public void EvaluatePolicy_PreflightRequest_HeadersRequested_NotAllHeaderMatches_ReturnsInvalidResult() { CorsEngine corsEngine = new CorsEngine(); CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = "OPTIONS", AccessControlRequestMethod = "PUT", Origin = "foo" }; requestContext.AccessControlRequestHeaders.Add("match"); requestContext.AccessControlRequestHeaders.Add("noMatch"); CorsPolicy policy = new CorsPolicy { AllowAnyOrigin = true, AllowAnyMethod = true }; policy.Headers.Add("match"); policy.Headers.Add("foo"); CorsResult result = corsEngine.EvaluatePolicy(requestContext, policy); Assert.True(requestContext.IsPreflight); Assert.False(result.IsValid); Assert.Contains("The collection of headers 'match,noMatch' is not allowed.", result.ErrorMessages); } [Fact] public void TryValidateMethod_NullPolicy_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateMethod(new CorsRequestContext(), null, new CorsResult()), "policy"); } [Fact] public void TryValidateMethod_NullRequestContext_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateMethod(null, new CorsPolicy(), new CorsResult()), "requestContext"); } [Fact] public void TryValidateMethod_NullResult_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateMethod(new CorsRequestContext(), new CorsPolicy(), null), "result"); } [Fact] public void TryValidateMethod_DoesCaseSensitiveComparison() { CorsEngine corsEngine = new CorsEngine(); CorsPolicy policy = new CorsPolicy(); policy.Methods.Add("POST"); CorsResult result = new CorsResult(); bool isValid = corsEngine.TryValidateMethod(new CorsRequestContext { AccessControlRequestMethod = "post" }, policy, result); Assert.False(isValid); Assert.Equal(1, result.ErrorMessages.Count); Assert.Equal("The method 'post' is not allowed.", result.ErrorMessages[0]); } [Fact] public void TryValidateHeaders_NullPolicy_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateHeaders(new CorsRequestContext(), null, new CorsResult()), "policy"); } [Fact] public void TryValidateHeaders_NullRequestContext_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateHeaders(null, new CorsPolicy(), new CorsResult()), "requestContext"); } [Fact] public void TryValidateHeaders_NullResult_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateHeaders(new CorsRequestContext(), new CorsPolicy(), null), "result"); } [Fact] public void TryValidateOrigin_NullPolicy_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateOrigin(new CorsRequestContext(), null, new CorsResult()), "policy"); } [Fact] public void TryValidateOrigin_NullRequestContext_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateOrigin(null, new CorsPolicy(), new CorsResult()), "requestContext"); } [Fact] public void TryValidateOrigin_NullResult_Throws() { CorsEngine corsEngine = new CorsEngine(); Assert.ThrowsArgumentNull(() => corsEngine.TryValidateOrigin(new CorsRequestContext(), new CorsPolicy(), null), "result"); } [Fact] public void TryValidateOrigin_DoesCaseSensitiveComparison() { CorsEngine corsEngine = new CorsEngine(); CorsPolicy policy = new CorsPolicy(); policy.Origins.Add("http://Example.com"); CorsResult result = new CorsResult(); bool isValid = corsEngine.TryValidateOrigin(new CorsRequestContext { Origin = "http://example.com" }, policy, result); Assert.False(isValid); Assert.Equal(1, result.ErrorMessages.Count); Assert.Equal("The origin 'http://example.com' is not allowed.", result.ErrorMessages[0]); } } } ================================================ FILE: test/System.Web.Cors.Test/CorsPolicyTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Web.Cors.Test { public class CorsPolicyTest { [Fact] public void Default_Constructor() { CorsPolicy corsPolicy = new CorsPolicy(); Assert.False(corsPolicy.AllowAnyHeader); Assert.False(corsPolicy.AllowAnyMethod); Assert.False(corsPolicy.AllowAnyOrigin); Assert.False(corsPolicy.SupportsCredentials); Assert.Empty(corsPolicy.ExposedHeaders); Assert.Empty(corsPolicy.Headers); Assert.Empty(corsPolicy.Methods); Assert.Empty(corsPolicy.Origins); Assert.Null(corsPolicy.PreflightMaxAge); } [Fact] public void SettingNegativePreflightMaxAge_Throws() { CorsPolicy corsPolicy = new CorsPolicy(); Assert.ThrowsArgumentOutOfRange(() => { corsPolicy.PreflightMaxAge = -2; }, "value", "PreflightMaxAge must be greater than or equal to 0."); } [Fact] public void ToString_ReturnsThePropertyValues() { CorsPolicy corsPolicy = new CorsPolicy { AllowAnyHeader = true, AllowAnyOrigin = true, PreflightMaxAge = 10, SupportsCredentials = true }; corsPolicy.Headers.Add("foo"); corsPolicy.Headers.Add("bar"); corsPolicy.Origins.Add("http://example.com"); corsPolicy.Origins.Add("http://example.org"); corsPolicy.Methods.Add("GET"); Assert.Equal(@"AllowAnyHeader: True, AllowAnyMethod: False, AllowAnyOrigin: True, PreflightMaxAge: 10, SupportsCredentials: True, Origins: {http://example.com,http://example.org}, Methods: {GET}, Headers: {foo,bar}, ExposedHeaders: {}", corsPolicy.ToString()); } } } ================================================ FILE: test/System.Web.Cors.Test/CorsRequestContextTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.TestCommon; namespace System.Web.Cors.Test { public class CorsRequestContextTest { [Fact] public void Default_Constructor() { CorsRequestContext requestContext = new CorsRequestContext(); Assert.Null(requestContext.AccessControlRequestMethod); Assert.Null(requestContext.Host); Assert.Null(requestContext.HttpMethod); Assert.Null(requestContext.Origin); Assert.Null(requestContext.RequestUri); Assert.NotNull(requestContext.AccessControlRequestHeaders); Assert.False(requestContext.IsPreflight); } [Theory] [InlineData("OPTIONS", "POST", "foo")] [InlineData("options", "POST", "foo")] [InlineData("OPTIONS", "GET", "foo")] [InlineData("OPTIONS", "OPTIONS", "")] public void IsPreflight_ReturnsTrue(string httpMethod, string requestedMethod, string origin) { CorsRequestContext requestContext = new CorsRequestContext { HttpMethod = httpMethod, AccessControlRequestMethod = requestedMethod, Origin = origin }; Assert.True(requestContext.IsPreflight); } [Theory] [InlineData("OPTIONS", "POST", null)] [InlineData("options", "POST", null)] [InlineData("OPTIONS", null, "foo")] [InlineData(null, "POST", "foo")] [InlineData("POST", "GET", "bar")] public void IsPreflight_ReturnsFalse(string httpMethod, string requestedMethod, string origin) { CorsRequestContext requestContext = new CorsRequestContext() { HttpMethod = httpMethod, AccessControlRequestMethod = requestedMethod, Origin = origin }; Assert.False(requestContext.IsPreflight); } [Fact] public void ToString_ReturnsThePropertyValues() { CorsRequestContext requestContext = new CorsRequestContext { Host = "http://example.com", HttpMethod = "OPTIONS", AccessControlRequestMethod = "DELETE", Origin = "http://localhost", RequestUri = new Uri("http://example.com") }; requestContext.AccessControlRequestHeaders.Add("foo"); requestContext.AccessControlRequestHeaders.Add("bar"); Assert.Equal(@"Origin: http://localhost, HttpMethod: OPTIONS, IsPreflight: True, Host: http://example.com, AccessControlRequestMethod: DELETE, RequestUri: http://example.com/, AccessControlRequestHeaders: {foo,bar}", requestContext.ToString()); } } } ================================================ FILE: test/System.Web.Cors.Test/CorsResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using Microsoft.TestCommon; namespace System.Web.Cors.Test { public class CorsResultTest { [Fact] public void Default_Constructor() { CorsResult result = new CorsResult(); Assert.Empty(result.AllowedHeaders); Assert.Empty(result.AllowedExposedHeaders); Assert.Empty(result.AllowedMethods); Assert.Empty(result.ErrorMessages); Assert.False(result.SupportsCredentials); Assert.Null(result.AllowedOrigin); Assert.Null(result.PreflightMaxAge); Assert.True(result.IsValid); } [Fact] public void SettingNegativePreflightMaxAge_Throws() { CorsResult result = new CorsResult(); Assert.ThrowsArgumentOutOfRange(() => { result.PreflightMaxAge = -2; }, "value", "PreflightMaxAge must be greater than or equal to 0."); } [Fact] public void IsValid_ReturnsFalse_WhenThereIsError() { CorsResult result = new CorsResult(); result.ErrorMessages.Add("error"); Assert.False(result.IsValid); } [Fact] public void ToResponseHeaders_ReturnsNoHeaders_ByDefault() { CorsResult result = new CorsResult(); IDictionary headers = result.ToResponseHeaders(); Assert.Empty(headers); } [Fact] public void ToResponseHeaders_AllowOrigin_AllowOriginHeaderAdded() { CorsResult result = new CorsResult { AllowedOrigin = "http://example.com" }; IDictionary headers = result.ToResponseHeaders(); Assert.Equal("http://example.com", headers["Access-Control-Allow-Origin"]); } [Fact] public void ToResponseHeaders_NoAllowOrigin_AllowOriginHeaderNotAdded() { CorsResult result = new CorsResult { AllowedOrigin = null }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Origin", headers.Keys); } [Fact] public void ToResponseHeaders_AllowCredentials_AllowCredentialsHeaderAdded() { CorsResult result = new CorsResult { SupportsCredentials = true }; IDictionary headers = result.ToResponseHeaders(); Assert.Equal("true", headers["Access-Control-Allow-Credentials"]); } [Fact] public void ToResponseHeaders_NoAllowCredentials_AllowCredentialsHeaderNotAdded() { CorsResult result = new CorsResult { SupportsCredentials = false }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Credentials", headers.Keys); } [Fact] public void ToResponseHeaders_NoAllowMethods_AllowMethodsHeaderNotAdded() { CorsResult result = new CorsResult { // AllowMethods is empty by default }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Methods", headers.Keys); } [Fact] public void ToResponseHeaders_OneAllowMethods_AllowMethodsHeaderAdded() { CorsResult result = new CorsResult(); result.AllowedMethods.Add("PUT"); IDictionary headers = result.ToResponseHeaders(); Assert.Equal("PUT", headers["Access-Control-Allow-Methods"]); } [Fact] public void ToResponseHeaders_SomeSimpleAllowMethods_AllowMethodsHeaderAddedForNonSimpleMethods() { CorsResult result = new CorsResult(); result.AllowedMethods.Add("PUT"); result.AllowedMethods.Add("get"); result.AllowedMethods.Add("DELETE"); result.AllowedMethods.Add("POST"); IDictionary headers = result.ToResponseHeaders(); Assert.Contains("Access-Control-Allow-Methods", headers.Keys); string[] methods = headers["Access-Control-Allow-Methods"].Split(','); Assert.Equal(2, methods.Length); Assert.Contains("PUT", methods); Assert.Contains("DELETE", methods); } [Fact] public void ToResponseHeaders_SimpleAllowMethods_AllowMethodsHeaderNotAdded() { CorsResult result = new CorsResult(); result.AllowedMethods.Add("GET"); result.AllowedMethods.Add("HEAD"); result.AllowedMethods.Add("POST"); IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Methods", headers.Keys); } [Fact] public void ToResponseHeaders_NoAllowHeaders_AllowHeadersHeaderNotAdded() { CorsResult result = new CorsResult { // AllowHeaders is empty by default }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Headers", headers.Keys); } [Fact] public void ToResponseHeaders_OneAllowHeaders_AllowHeadersHeaderAdded() { CorsResult result = new CorsResult(); result.AllowedHeaders.Add("foo"); IDictionary headers = result.ToResponseHeaders(); Assert.Equal("foo", headers["Access-Control-Allow-Headers"]); } [Fact] public void ToResponseHeaders_ManyAllowHeaders_AllowHeadersHeaderAdded() { CorsResult result = new CorsResult(); result.AllowedHeaders.Add("foo"); result.AllowedHeaders.Add("bar"); result.AllowedHeaders.Add("baz"); IDictionary headers = result.ToResponseHeaders(); Assert.Contains("Access-Control-Allow-Headers", headers.Keys); string[] headerValues = headers["Access-Control-Allow-Headers"].Split(','); Assert.Equal(3, headerValues.Length); Assert.Contains("foo", headerValues); Assert.Contains("bar", headerValues); Assert.Contains("baz", headerValues); } [Fact] public void ToResponseHeaders_SomeSimpleAllowHeaders_AllowHeadersHeaderAddedForNonSimpleHeaders() { CorsResult result = new CorsResult(); result.AllowedHeaders.Add("Content-Language"); result.AllowedHeaders.Add("foo"); result.AllowedHeaders.Add("bar"); result.AllowedHeaders.Add("Accept"); IDictionary headers = result.ToResponseHeaders(); Assert.Contains("Access-Control-Allow-Headers", headers.Keys); string[] headerValues = headers["Access-Control-Allow-Headers"].Split(','); Assert.Equal(2, headerValues.Length); Assert.Contains("foo", headerValues); Assert.Contains("bar", headerValues); } [Fact] public void ToResponseHeaders_SimpleAllowHeaders_AllowHeadersHeaderNotAdded() { CorsResult result = new CorsResult(); result.AllowedHeaders.Add("Accept"); result.AllowedHeaders.Add("Accept-Language"); result.AllowedHeaders.Add("Content-Language"); IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Allow-Headers", headers.Keys); } [Fact] public void ToResponseHeaders_NoAllowExposedHeaders_ExposedHeadersHeaderNotAdded() { CorsResult result = new CorsResult { // AllowExposedHeaders is empty by default }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Expose-Headers", headers.Keys); } [Fact] public void ToResponseHeaders_OneAllowExposedHeaders_ExposedHeadersHeaderAdded() { CorsResult result = new CorsResult(); result.AllowedExposedHeaders.Add("foo"); IDictionary headers = result.ToResponseHeaders(); Assert.Equal("foo", headers["Access-Control-Expose-Headers"]); } [Fact] public void ToResponseHeaders_ManyAllowExposedHeaders_ExposedHeadersHeaderAdded() { CorsResult result = new CorsResult(); result.AllowedExposedHeaders.Add("foo"); result.AllowedExposedHeaders.Add("bar"); result.AllowedExposedHeaders.Add("baz"); IDictionary headers = result.ToResponseHeaders(); Assert.Contains("Access-Control-Expose-Headers", headers.Keys); string[] exposedHeaderValues = headers["Access-Control-Expose-Headers"].Split(','); Assert.Equal(3, exposedHeaderValues.Length); Assert.Contains("foo", exposedHeaderValues); Assert.Contains("bar", exposedHeaderValues); Assert.Contains("baz", exposedHeaderValues); } [Fact] public void ToResponseHeaders_NoPreflightMaxAge_MaxAgeHeaderNotAdded() { CorsResult result = new CorsResult { PreflightMaxAge = null }; IDictionary headers = result.ToResponseHeaders(); Assert.DoesNotContain("Access-Control-Max-Age", headers.Keys); } [Fact] public void ToResponseHeaders_PreflightMaxAge_MaxAgeHeaderAdded() { CorsResult result = new CorsResult { PreflightMaxAge = 30 }; IDictionary headers = result.ToResponseHeaders(); Assert.Equal("30", headers["Access-Control-Max-Age"]); } [Fact] public void ToString_ReturnsThePropertyValues() { CorsResult corsResult = new CorsResult { SupportsCredentials = true, PreflightMaxAge = 20, AllowedOrigin = "*" }; corsResult.AllowedExposedHeaders.Add("foo"); corsResult.AllowedHeaders.Add("bar"); corsResult.AllowedHeaders.Add("baz"); corsResult.AllowedMethods.Add("GET"); corsResult.ErrorMessages.Add("error1"); corsResult.ErrorMessages.Add("error2"); Assert.Equal(@"IsValid: False, AllowCredentials: True, PreflightMaxAge: 20, AllowOrigin: *, AllowExposedHeaders: {foo}, AllowHeaders: {bar,baz}, AllowMethods: {GET}, ErrorMessages: {error1,error2}", corsResult.ToString()); } } } ================================================ FILE: test/System.Web.Cors.Test/System.Web.Cors.Test.csproj ================================================  Library Properties System.Web.Cors System.Web.Cors.Test ..\..\bin\$(Configuration)\Test\ {BF07E947-120D-4E93-93DA-A4BF121753EA} ..\..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll True ..\..\packages\Moq.4.18.4\lib\net462\Moq.dll True ..\..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.4.2\lib\netstandard1.1\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.4.2\lib\net452\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.4.2\lib\net452\xunit.execution.desktop.dll True {43c1b979-d593-4a32-bb3a-4316f1c66d66} System.Web.Cors {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} Microsoft.TestCommon This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: test/System.Web.Cors.Test/packages.config ================================================  ================================================ FILE: test/System.Web.Helpers.Test/ChartTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Drawing; using System.IO; using System.Web.Hosting; using System.Web.UI.DataVisualization.Charting; using Microsoft.TestCommon; using Moq; namespace System.Web.Helpers.Test { public class ChartTest { private byte[] _writeData; public ChartTest() { _writeData = null; } [Fact] public void BuildChartAddsDefaultArea() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); AssertBuiltChartAction(chart, c => { ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.Equal("Default", chartArea.Name); }); } [Fact] public void XAxisOverrides() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .SetXAxis("AxisX", 1, 100); AssertBuiltChartAction(chart, c => { ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.Equal("AxisX", chartArea.AxisX.Title); Assert.Equal(1, chartArea.AxisX.Minimum); Assert.Equal(100, chartArea.AxisX.Maximum); }); } [Fact] public void YAxisOverrides() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .SetYAxis("AxisY", 1, 100); AssertBuiltChartAction(chart, c => { ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.Equal("AxisY", chartArea.AxisY.Title); Assert.Equal(1, chartArea.AxisY.Minimum); Assert.Equal(100, chartArea.AxisY.Maximum); }); } [Fact] public void ConstructorLoadsTemplate() { var template = WriteTemplate(@""); var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, themePath: template); AssertBuiltChartAction(chart, c => { Assert.Equal(2, c.BorderWidth); }); } [Fact] public void ConstructorLoadsTheme() { //Vanilla theme /* * */ var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, theme: ChartTheme.Vanilla); AssertBuiltChartAction(chart, c => { Assert.Equal(ChartColorPalette.SemiTransparent, c.Palette); Assert.Equal(c.BorderColor, Color.FromArgb(0, Color.Black)); ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.False(chartArea.AxisX.MajorGrid.Enabled); Assert.False(chartArea.AxisY.MinorGrid.Enabled); }); } [Fact] public void ConstructorLoadsThemeAndTemplate() { //Vanilla theme /* * */ var template = WriteTemplate(@""); var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, theme: ChartTheme.Vanilla, themePath: template); AssertBuiltChartAction(chart, c => { Assert.Equal(ChartColorPalette.SemiTransparent, c.Palette); Assert.Equal(c.BorderColor, Color.FromArgb(0, Color.Black)); Assert.Equal(ChartDashStyle.DashDot, c.BorderlineDashStyle); Legend legend = Assert.Single(c.Legends); Assert.Equal(legend.BackColor, Color.Red); ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.False(chartArea.AxisX.MajorGrid.Enabled); Assert.False(chartArea.AxisY.MinorGrid.Enabled); }); } [Fact] public void ConstructorLoadsThemeAndTemplate_VPPRegistrationChanging() { // Arrange // Vanilla theme, as in ConstructorLoadsThemeAndTemplate() string template = WriteTemplate(@""); HttpContextBase context = GetContext(); string templatePath = VirtualPathUtility.Combine(context.Request.AppRelativeCurrentExecutionFilePath, template); MockVirtualPathProvider provider1 = new MockVirtualPathProvider(); MockVirtualPathProvider provider2 = new MockVirtualPathProvider(); VirtualPathProvider provider = provider1; // Act; use one provider in constructor and another in ExecuteChartAction() Chart chart = new Chart(context, () => provider, 100, 100, theme: ChartTheme.Vanilla, themePath: template); // The moral equivalent of HostingEnvironment.RegisterVirtualPathProvider(provider2) provider = provider2; // Assert AssertBuiltChartAction(chart, c => { Assert.Equal(ChartColorPalette.SemiTransparent, c.Palette); Assert.Equal(c.BorderColor, Color.FromArgb(0, Color.Black)); Assert.Equal(ChartDashStyle.DashDot, c.BorderlineDashStyle); Legend legend = Assert.Single(c.Legends); Assert.Equal(legend.BackColor, Color.Red); ChartArea chartArea = Assert.Single(c.ChartAreas); Assert.False(chartArea.AxisX.MajorGrid.Enabled); Assert.False(chartArea.AxisY.MinorGrid.Enabled); }); Assert.Equal(1, provider1.FileExistsCalls); Assert.Equal(0, provider1.GetFileCalls); Assert.Equal(0, provider2.FileExistsCalls); Assert.Equal(1, provider2.GetFileCalls); } [Fact] public void ConstructorSetsWidthAndHeight() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 101, 102); Assert.Equal(101, chart.Width); Assert.Equal(102, chart.Height); AssertBuiltChartAction(chart, c => { Assert.Equal(101, c.Width); Assert.Equal(102, c.Height); }); } [Fact] public void ConstructorThrowsWhenHeightIsLessThanZero() { Assert.ThrowsArgumentOutOfRange(() => { new Chart(GetContext(), GetVirtualPathProvider(), 100, -1); }, "height", "Value must be greater than or equal to 0."); } [Fact] public void ConstructorThrowsWhenTemplateNotFound() { var templateFile = @"FileNotFound.xml"; Assert.ThrowsArgument(() => { new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, themePath: templateFile); }, "themePath", String.Format("The theme file \"{0}\" could not be found.", VirtualPathUtility.Combine(GetContext().Request.AppRelativeCurrentExecutionFilePath, templateFile))); } [Fact] public void ConstructorThrowsWhenWidthIsLessThanZero() { Assert.ThrowsArgumentOutOfRange(() => { new Chart(GetContext(), GetVirtualPathProvider(), -1, 100); }, "width", "Value must be greater than or equal to 0."); } [Fact] public void DataBindCrossTable() { var data = new[] { new { GroupBy = "1", YValue = 1 }, new { GroupBy = "1", YValue = 2 }, new { GroupBy = "2", YValue = 1 } }; var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .DataBindCrossTable(data, "GroupBy", xField: null, yFields: "YValue"); // todo - anything else to verify here? AssertBuiltChartAction(chart, c => { Assert.Equal(2, c.Series.Count); Assert.Equal(2, c.Series[0].Points.Count); Assert.Single(c.Series[1].Points); }); } [Fact] public void DataBindCrossTableThrowsWhenDataSourceIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNull(() => { chart.DataBindCrossTable(null, "GroupBy", xField: null, yFields: "yFields"); }, "dataSource"); } [Fact] public void DataBindCrossTableThrowsWhenDataSourceIsString() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgument(() => { chart.DataBindCrossTable("DataSource", "GroupBy", xField: null, yFields: "yFields"); }, "dataSource", "A series cannot be data-bound to a string object."); } [Fact] public void DataBindCrossTableThrowsWhenGroupByIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.DataBindCrossTable(new object[0], null, xField: null, yFields: "yFields"); }, "groupByField"); } [Fact] public void DataBindCrossTableThrowsWhenGroupByIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.DataBindCrossTable(new object[0], "", xField: null, yFields: "yFields"); }, "groupByField"); } [Fact] public void DataBindCrossTableThrowsWhenYFieldsIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.DataBindCrossTable(new object[0], "GroupBy", xField: null, yFields: null); }, "yFields"); } [Fact] public void DataBindCrossTableThrowsWhenYFieldsIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.DataBindCrossTable(new object[0], "GroupBy", xField: null, yFields: ""); }, "yFields"); } [Fact] public void DataBindTable() { var data = new[] { new { XValue = "1", YValue = 1 }, new { XValue = "2", YValue = 2 }, new { XValue = "3", YValue = 3 } }; var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .DataBindTable(data, xField: "XValue"); // todo - anything else to verify here? AssertBuiltChartAction(chart, c => { var series = Assert.Single(c.Series); Assert.Equal(3, series.Points.Count); }); } [Fact] public void DataBindTableWhenXFieldIsNull() { var data = new[] { new { YValue = 1 }, new { YValue = 2 }, new { YValue = 3 } }; var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .DataBindTable(data, xField: null); // todo - anything else to verify here? AssertBuiltChartAction(chart, c => { var series = Assert.Single(c.Series); Assert.Equal(3, series.Points.Count); }); } [Fact] public void DataBindTableThrowsWhenDataSourceIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNull(() => { chart.DataBindTable(null); }, "dataSource"); } [Fact] public void DataBindTableThrowsWhenDataSourceIsString() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgument(() => { chart.DataBindTable(""); }, "dataSource", "A series cannot be data-bound to a string object."); } [Fact] public void GetBytesReturnsNonEmptyArray() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.True(chart.GetBytes().Length > 0); } [Fact] public void GetBytesThrowsWhenFormatIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.GetBytes(format: String.Empty); }, "format"); } [Fact] public void GetBytesThrowsWhenFormatIsInvalid() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgument(() => { chart.GetBytes(format: "foo"); }, "format", "\"foo\" is invalid image format. Valid values are image format names like: \"JPEG\", \"BMP\", \"GIF\", \"PNG\", etc."); } [Fact] public void GetBytesThrowsWhenFormatIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.GetBytes(format: null); }, "format"); } [Fact] public void LegendDefaults() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100).AddLegend(); AssertBuiltChartAction(chart, c => { Legend legend = Assert.Single(c.Legends); // NOTE: Chart.Legends.Add will create default name Assert.Equal("Legend1", legend.Name); Assert.Equal(1, legend.BorderWidth); }); } [Fact] public void LegendOverrides() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100).AddLegend("Legend1") .AddLegend("Legend2", "Legend2Name"); AssertBuiltChartAction(chart, c => { Assert.Equal(2, c.Legends.Count); Assert.Equal("Legend1", c.Legends[0].Name); Assert.Equal("Legend2", c.Legends[1].Title); Assert.Equal("Legend2Name", c.Legends[1].Name); }); } [Fact] public void SaveAndWriteFromCache() { var context1 = GetContext(); var chart = new Chart(context1, GetVirtualPathProvider(), 100, 100); string key = chart.SaveToCache(); Assert.Equal(chart, WebCache.Get(key)); var context2 = GetContext(); Assert.Equal(chart, Chart.GetFromCache(context2, key)); Chart.WriteFromCache(context2, key); Assert.Null(context1.Response.ContentType); Assert.Equal("image/jpeg", context2.Response.ContentType); } [Fact] public void SaveThrowsWhenFormatIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.Save(GetContext(), "chartPath", format: String.Empty); }, "format"); } [Fact] public void SaveWorksWhenFormatIsJPG() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); string fileName = "chartPath"; chart.Save(GetContext(), "chartPath", format: "jpg"); byte[] a = File.ReadAllBytes(fileName); chart.Save(GetContext(), "chartPath", format: "jpeg"); byte[] b = File.ReadAllBytes(fileName); Assert.Equal(a, b); } [Fact] public void SaveThrowsWhenFormatIsInvalid() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgument(() => { chart.Save(GetContext(), "chartPath", format: "foo"); }, "format", "\"foo\" is invalid image format. Valid values are image format names like: \"JPEG\", \"BMP\", \"GIF\", \"PNG\", etc."); } [Fact] public void SaveThrowsWhenFormatIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.Save(GetContext(), "chartPath", format: null); }, "format"); } [Fact] public void SaveThrowsWhenPathIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.Save(GetContext(), path: String.Empty, format: "jpeg"); }, "path"); } [Fact] public void SaveThrowsWhenPathIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.Save(GetContext(), path: null, format: "jpeg"); }, "path"); } [Fact] public void SaveWritesToFile() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); chart.Save(GetContext(), "SaveWritesToFile.jpg", format: "image/jpeg"); Assert.Equal("SaveWritesToFile.jpg", Path.GetFileName(chart.FileName)); Assert.True(File.Exists(chart.FileName)); } [Fact] public void SaveXmlThrowsWhenPathIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.SaveXml(GetContext(), String.Empty); }, "path"); } [Fact] public void SaveXmlThrowsWhenPathIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.SaveXml(GetContext(), null); }, "path"); } [Fact] public void SaveXmlWritesToFile() { var template = WriteTemplate(@""); var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, themePath: template); chart.SaveXml(GetContext(), "SaveXmlWritesToFile.xml"); Assert.True(File.Exists("SaveXmlWritesToFile.xml")); string result = File.ReadAllText("SaveXmlWritesToFile.xml"); Assert.Contains("BorderWidth=\"2\"", result); } [Fact] public void TemplateWithCommentsDoesNotThrow() { var template = WriteTemplate(@" "); var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, themePath: template); Assert.NotNull(chart.ToWebImage()); } [Fact] public void TemplateWithIncorrectPropertiesThrows() { var template = WriteTemplate(@""); var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100, themePath: template); Assert.Throws(() => chart.ToWebImage(), "Cannot deserialize property. Unknown property name 'borderWidth' in object \" System.Web.UI.DataVisualization.Charting.Chart"); } [Fact] public void WriteWorksWithJPGFormat() { var response = new Mock(); var stream = new MemoryStream(); response.Setup(c => c.Output).Returns(new StreamWriter(stream)); var context = new Mock(); context.Setup(c => c.Response).Returns(response.Object); var chart = new Chart(context.Object, GetVirtualPathProvider(), 100, 100); chart.Write("jpeg"); byte[] a = stream.GetBuffer(); stream.SetLength(0); chart.Write("jpg"); byte[] b = stream.GetBuffer(); Assert.Equal(a, b); } [Fact] public void WriteThrowsWithInvalidFormat() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgument(() => chart.Write("foo"), "format", "\"foo\" is invalid image format. Valid values are image format names like: \"JPEG\", \"BMP\", \"GIF\", \"PNG\", etc."); } [Fact] public void SeriesOverrides() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100) .AddSeries(chartType: "Bar"); AssertBuiltChartAction(chart, c => { var series = Assert.Single(c.Series); Assert.Equal(SeriesChartType.Bar, series.ChartType); }); } [Fact] public void SeriesThrowsWhenChartTypeIsEmpty() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.AddSeries(chartType: ""); }, "chartType"); } [Fact] public void SeriesThrowsWhenChartTypeIsNull() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); Assert.ThrowsArgumentNullOrEmptyString(() => { chart.AddSeries(chartType: null); }, "chartType"); } [Fact] public void TitleDefaults() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100).AddTitle(); AssertBuiltChartAction(chart, c => { var title = Assert.Single(c.Titles); // NOTE: Chart.Titles.Add will create default name Assert.Equal("Title1", title.Name); Assert.Equal(String.Empty, title.Text); Assert.Equal(1, title.BorderWidth); }); } [Fact] public void TitleOverrides() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100).AddTitle(name: "Title1") .AddTitle("Title2Text", name: "Title2"); AssertBuiltChartAction(chart, c => { Assert.Equal(2, c.Titles.Count); Assert.Equal("Title1", c.Titles[0].Name); Assert.Equal("Title2", c.Titles[1].Name); Assert.Equal("Title2Text", c.Titles[1].Text); }); } [Fact] public void ToWebImage() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); var image = chart.ToWebImage(); Assert.NotNull(image); Assert.Equal("jpeg", image.ImageFormat); } [Fact] public void ToWebImageUsesFormat() { var chart = new Chart(GetContext(), GetVirtualPathProvider(), 100, 100); var image = chart.ToWebImage(format: "png"); Assert.NotNull(image); Assert.Equal("png", image.ImageFormat); } [Fact] public void WriteFromCacheIsNoOpIfNotSavedInCache() { var context = GetContext(); Assert.Null(Chart.WriteFromCache(context, Guid.NewGuid().ToString())); Assert.Null(context.Response.ContentType); } [Fact] public void WriteUpdatesResponse() { var context = GetContext(); var chart = new Chart(context, GetVirtualPathProvider(), 100, 100); chart.Write(); Assert.Equal("", context.Response.Charset); Assert.Equal("image/jpeg", context.Response.ContentType); Assert.True((_writeData != null) && (_writeData.Length > 0)); } private static void AssertBuiltChartAction(Chart chart, Action action) { bool actionCalled = false; chart.ExecuteChartAction(c => { action(c); actionCalled = true; }); Assert.True(actionCalled); } private HttpContextBase GetContext() { // Strip drive letter for VirtualPathUtility.Combine var testPath = Directory.GetCurrentDirectory().Substring(2) + "/Out"; Mock request = new Mock(); request.Setup(r => r.AppRelativeCurrentExecutionFilePath).Returns(testPath); request.Setup(r => r.MapPath(It.IsAny())).Returns((string path) => path); Mock response = new Mock(); response.SetupProperty(r => r.ContentType); response.SetupProperty(r => r.Charset); response.Setup(r => r.BinaryWrite(It.IsAny())).Callback((byte[] data) => _writeData = data); Mock server = new Mock(); server.Setup(s => s.MapPath(It.IsAny())).Returns((string s) => s); var items = new Hashtable(); Mock context = new Mock(); context.Setup(c => c.Request).Returns(request.Object); context.Setup(c => c.Response).Returns(response.Object); context.Setup(c => c.Server).Returns(server.Object); context.Setup(c => c.Items).Returns(items); return context.Object; } private static string WriteTemplate(string xml) { var path = Guid.NewGuid() + ".xml"; File.WriteAllText(path, xml); return path; } private static MockVirtualPathProvider GetVirtualPathProvider() { return new MockVirtualPathProvider(); } class MockVirtualPathProvider : VirtualPathProvider { public int FileExistsCalls { get; private set; } public int GetFileCalls { get; private set; } class MockVirtualFile : VirtualFile { public MockVirtualFile(string virtualPath) : base(virtualPath) { } public override Stream Open() { return File.Open(this.VirtualPath, FileMode.Open); } } public override bool FileExists(string virtualPath) { ++FileExistsCalls; return File.Exists(virtualPath); } public override VirtualFile GetFile(string virtualPath) { ++GetFileCalls; return new MockVirtualFile(virtualPath); } } } } ================================================ FILE: test/System.Web.Helpers.Test/ConversionUtilTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Drawing; using System.Globalization; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { public class ConversionUtilTest { [Fact] public void ConversionUtilReturnsStringTypes() { // Arrange string original = "Foo"; // Act object result; bool success = ConversionUtil.TryFromString(typeof(String), original, out result); // Assert Assert.True(success); Assert.Equal(original, result); } [Fact] public void ConversionUtilConvertsStringsToColor() { // Arrange string original = "Blue"; // Act object result; bool success = ConversionUtil.TryFromString(typeof(Color), original, out result); // Assert Assert.True(success); Assert.Equal(Color.Blue, result); } [Fact] public void ConversionUtilConvertsEnumValues() { // Arrange string original = "Weekday"; // Act object result; bool success = ConversionUtil.TryFromString(typeof(TestEnum), original, out result); // Assert Assert.True(success); Assert.Equal(TestEnum.Weekday, result); } [Fact] public void ConversionUtilUsesTypeConverterToConvertArbitraryTypes() { // Arrange var date = new DateTime(2010, 01, 01); string original = date.ToString(CultureInfo.InvariantCulture); // Act object result; bool success = ConversionUtil.TryFromString(typeof(DateTime), original, out result); // Assert Assert.True(success); Assert.Equal(date, result); } private enum TestEnum { Weekend, Weekday } } } ================================================ FILE: test/System.Web.Helpers.Test/CryptoTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Security.Cryptography; using System.Text; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { /// /// This is a test class for CryptoTest and is intended /// to contain all CryptoTest Unit Tests /// public class CryptoTest { [Fact] public void SHA256HashTest_ReturnsValidData() { string data = "foo bar"; string expected = "FBC1A9F858EA9E177916964BD88C3D37B91A1E84412765E29950777F265C4B75"; string actual; actual = Crypto.SHA256(data); Assert.Equal(expected, actual); actual = Crypto.Hash(Encoding.UTF8.GetBytes(data)); Assert.Equal(expected, actual); } [Fact] public void GenerateSaltTest() { string salt = Crypto.GenerateSalt(); salt = Crypto.GenerateSalt(64); Assert.Equal(24, Crypto.GenerateSalt().Length); Assert.Equal(12, Crypto.GenerateSalt(8).Length); Assert.Equal(88, Crypto.GenerateSalt(64).Length); Assert.Equal(44, Crypto.GenerateSalt(32).Length); } [Fact] public void HashPassword_PasswordGeneration() { // Act - call helper directly string generatedHash = Crypto.HashPassword("my-password"); byte[] salt = new byte[16]; Buffer.BlockCopy(Convert.FromBase64String(generatedHash), 1, salt, 0, 16); // extract salt from generated hash // Act - perform PBKDF2 directly string generatedHash2; using (var ms = new MemoryStream()) { using (var bw = new BinaryWriter(ms)) { using (var deriveBytes = new Rfc2898DeriveBytes("my-password", salt, iterations: 1000)) { bw.Write((byte)0x00); // version identifier bw.Write(salt); // salt bw.Write(deriveBytes.GetBytes(32)); // subkey } generatedHash2 = Convert.ToBase64String(ms.ToArray()); } } // Assert Assert.Equal(generatedHash2, generatedHash); } [Fact] public void HashPassword_RoundTripping() { // Act & assert string password = "ImPepper"; Assert.True(Crypto.VerifyHashedPassword(Crypto.HashPassword(password), password)); Assert.False(Crypto.VerifyHashedPassword(Crypto.HashPassword(password), "ImSalt")); Assert.False(Crypto.VerifyHashedPassword(Crypto.HashPassword("Impepper"), password)); } [Fact] public void VerifyHashedPassword_CorrectPassword_ReturnsTrue() { // Arrange string hashedPassword = "ALyuoraY/cIWD1hjo+K81/pf83qo6Q6T+UBYcXN9P3A9WHLvEY10f+lwW5qPG6h9xw=="; // this is for 'my-password' // Act bool retVal = Crypto.VerifyHashedPassword(hashedPassword, "my-password"); // Assert Assert.True(retVal); } [Fact] public void VerifyHashedPassword_IncorrectPassword_ReturnsFalse() { // Arrange string hashedPassword = "ALyuoraY/cIWD1hjo+K81/pf83qo6Q6T+UBYcXN9P3A9WHLvEY10f+lwW5qPG6h9xw=="; // this is for 'my-password' // Act bool retVal = Crypto.VerifyHashedPassword(hashedPassword, "some-other-password"); // Assert Assert.False(retVal); } [Fact] public void VerifyHashedPassword_InvalidPasswordHash_ReturnsFalse() { // Arrange string hashedPassword = "AAECAw=="; // this is an invalid password hash // Act bool retVal = Crypto.VerifyHashedPassword(hashedPassword, "hello-world"); // Assert Assert.False(retVal); } [Fact] public void MD5HashTest_ReturnsValidData() { string data = "foo bar"; string expected = "327B6F07435811239BC47E1544353273"; string actual; actual = Crypto.Hash(data, algorithm: "md5"); Assert.Equal(expected, actual); actual = Crypto.Hash(Encoding.UTF8.GetBytes(data), algorithm: "MD5"); Assert.Equal(expected, actual); } [Fact] public void SHA1HashTest_ReturnsValidData() { string data = "foo bar"; string expected = "3773DEA65156909838FA6C22825CAFE090FF8030"; string actual; actual = Crypto.SHA1(data); Assert.Equal(expected, actual); actual = Crypto.Hash(Encoding.UTF8.GetBytes(data), algorithm: "sha1"); Assert.Equal(expected, actual); } [Fact] public void SHA1HashTest_WithNull_ThrowsException() { Assert.Throws(() => Crypto.SHA1((string)null)); Assert.Throws(() => Crypto.Hash((byte[])null, algorithm: "SHa1")); } [Fact] public void SHA256HashTest_WithNull_ThrowsException() { Assert.Throws(() => Crypto.SHA256((string)null)); Assert.Throws(() => Crypto.Hash((byte[])null, algorithm: "sHa256")); } [Fact] public void MD5HashTest_WithNull_ThrowsException() { Assert.Throws(() => Crypto.Hash((string)null, algorithm: "mD5")); Assert.Throws(() => Crypto.Hash((byte[])null, algorithm: "mD5")); } [Fact] public void HashWithUnknownAlg_ThrowsException() { Assert.Throws(() => Crypto.Hash("sdflksd", algorithm: "hao"), "The hash algorithm 'hao' is not supported, valid values are: sha256, sha1, md5"); } } } ================================================ FILE: test/System.Web.Helpers.Test/DynamicDictionary.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; using System.Reflection; namespace System.Web.Helpers.Test { /// /// Dynamic object implementation over a dictionary that doesn't implement anything but the interface. /// Used for testing our types that consume dynamic objects to make sure they don't make any assumptions on the implementation. /// public class DynamicDictionary : IDynamicMetaObjectProvider { private readonly Dictionary _values = new Dictionary(); public object this[string name] { get { object result; _values.TryGetValue(name, out result); return result; } set { _values[name] = value; } } public DynamicMetaObject GetMetaObject(Expression parameter) { return new DynamicDictionaryMetaObject(parameter, this); } private class DynamicDictionaryMetaObject : DynamicMetaObject { private static readonly PropertyInfo ItemPropery = typeof(DynamicDictionary).GetProperty("Item"); public DynamicDictionaryMetaObject(Expression expression, object value) : base(expression, BindingRestrictions.Empty, value) { } private IDictionary WrappedDictionary { get { return ((DynamicDictionary)Value)._values; } } private Expression GetDynamicExpression() { return Expression.Convert(Expression, typeof(DynamicDictionary)); } private Expression GetIndexExpression(string key) { return Expression.MakeIndex( GetDynamicExpression(), ItemPropery, new[] { Expression.Constant(key) } ); } private Expression GetSetValueExpression(string key, object value) { return Expression.Assign( GetIndexExpression(key), Expression.Convert(Expression.Constant(value), typeof(object)) ); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { var binderDefault = binder.FallbackGetMember(this); var expression = Expression.Convert(GetIndexExpression(binder.Name), typeof(object)); var dynamicSuggestion = new DynamicMetaObject(expression, BindingRestrictions.GetTypeRestriction(Expression, LimitType) .Merge(binderDefault.Restrictions)); return binder.FallbackGetMember(this, dynamicSuggestion); } public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { var binderDefault = binder.FallbackSetMember(this, value); Expression expression = GetSetValueExpression(binder.Name, value.Value); var dynamicSuggestion = new DynamicMetaObject(expression, BindingRestrictions.GetTypeRestriction(Expression, LimitType) .Merge(binderDefault.Restrictions)); return binder.FallbackSetMember(this, value, dynamicSuggestion); } public override IEnumerable GetDynamicMemberNames() { return WrappedDictionary.Keys; } } } } ================================================ FILE: test/System.Web.Helpers.Test/DynamicHelperTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Dynamic; using Microsoft.Internal.Web.Utils; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { public class DynamicHelperTest { [Fact] public void TryGetMemberValueReturnsValueIfBinderIsNotCSharp() { // Arrange var mockMemberBinder = new MockMemberBinder("Foo"); var dynamic = new DynamicWrapper(new { Foo = "Bar" }); // Act object value; bool result = DynamicHelper.TryGetMemberValue(dynamic, mockMemberBinder, out value); // Assert Assert.Equal("Bar", value); } private class MockMemberBinder : GetMemberBinder { public MockMemberBinder(string name) : base(name, false) { } public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { throw new NotImplementedException(); } } } } ================================================ FILE: test/System.Web.Helpers.Test/DynamicWrapper.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace System.Web.Helpers.Test { /// /// Dynamic object implementation over a regualar CLR object. Getmember accesses members through reflection. /// public class DynamicWrapper : IDynamicMetaObjectProvider { private object _object; public DynamicWrapper(object obj) { _object = obj; } public DynamicMetaObject GetMetaObject(Expression parameter) { return new DynamicWrapperMetaObject(parameter, this); } private class DynamicWrapperMetaObject : DynamicMetaObject { public DynamicWrapperMetaObject(Expression expression, object value) : base(expression, BindingRestrictions.Empty, value) { } private object WrappedObject { get { return ((DynamicWrapper)Value)._object; } } private Expression GetDynamicExpression() { return Expression.Convert(Expression, typeof(DynamicWrapper)); } private Expression GetWrappedObjectExpression() { FieldInfo fieldInfo = typeof(DynamicWrapper).GetField("_object", BindingFlags.NonPublic | BindingFlags.Instance); Debug.Assert(fieldInfo != null); return Expression.Convert( Expression.Field(GetDynamicExpression(), fieldInfo), WrappedObject.GetType()); } private Expression GetMemberAccessExpression(string memberName) { return Expression.Property( GetWrappedObjectExpression(), memberName); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { var binderDefault = binder.FallbackGetMember(this); var expression = Expression.Convert(GetMemberAccessExpression(binder.Name), typeof(object)); var dynamicSuggestion = new DynamicMetaObject(expression, BindingRestrictions.GetTypeRestriction(Expression, LimitType) .Merge(binderDefault.Restrictions)); return binder.FallbackGetMember(this, dynamicSuggestion); } public override IEnumerable GetDynamicMemberNames() { return (from p in WrappedObject.GetType().GetProperties() orderby p.Name select p.Name).ToArray(); } } } } ================================================ FILE: test/System.Web.Helpers.Test/HelperResultTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.IO; using System.Web.WebPages; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { /// /// This is a test class for Util is intended /// to contain all HelperResult Unit Tests /// public class HelperResultTest { [Fact] public void HelperResultConstructorNullTest() { Assert.ThrowsArgumentNull(() => { var helper = new HelperResult(null); }, "action"); } [Fact] public void ToStringTest() { var text = "Hello"; Action action = tw => tw.Write(text); var helper = new HelperResult(action); Assert.Equal(text, helper.ToString()); } [Fact] public void WriteToTest() { var text = "Hello"; Action action = tw => tw.Write(text); var helper = new HelperResult(action); var writer = new StringWriter(); helper.WriteTo(writer); Assert.Equal(text, writer.ToString()); } [Fact] public void ToHtmlStringDoesNotEncode() { // Arrange string text = "This is a test & it uses html."; Action action = writer => writer.Write(text); HelperResult helperResult = new HelperResult(action); // Act string result = helperResult.ToHtmlString(); // Assert Assert.Equal(result, text); } [Fact] public void ToHtmlStringReturnsSameResultAsWriteTo() { // Arrange string text = "This is a test & it uses html."; Action action = writer => writer.Write(text); HelperResult helperResult = new HelperResult(action); StringWriter stringWriter = new StringWriter(); // Act string htmlString = helperResult.ToHtmlString(); helperResult.WriteTo(stringWriter); // Assert Assert.Equal(htmlString, stringWriter.ToString()); } } } ================================================ FILE: test/System.Web.Helpers.Test/JsonTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Dynamic; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { public class JsonTest { [Fact] public void EncodeWithDynamicObject() { // Arrange dynamic obj = new DummyDynamicObject(); obj.Name = "Hello"; obj.Age = 1; obj.Grades = new[] { "A", "B", "C" }; // Act string json = Json.Encode(obj); // Assert Assert.Equal("{\"Name\":\"Hello\",\"Age\":1,\"Grades\":[\"A\",\"B\",\"C\"]}", json); } [Fact] public void EncodeArray() { // Arrange object input = new string[] { "one", "2", "three", "4" }; // Act string json = Json.Encode(input); // Assert Assert.Equal("[\"one\",\"2\",\"three\",\"4\"]", json); } [Fact] public void EncodeDynamicJsonArrayEncodesAsArray() { // Arrange dynamic array = Json.Decode("[1,2,3]"); // Act string json = Json.Encode(array); // Assert Assert.Equal("[1,2,3]", json); } [Fact] public void EncodeObjectWithArrayProperty() { // Arrange dynamic personJson = Json.Decode("{name:'George', aliases: ['Peter', 'David']}"); // Act string json = Json.Encode(personJson); // Assert Assert.Equal("{\"name\":\"George\",\"aliases\":[\"Peter\",\"David\"]}", json); } [Fact] public void DecodeDynamicObject() { // Act var obj = Json.Decode("{\"Name\":\"Hello\",\"Age\":1,\"Grades\":[\"A\",\"B\",\"C\"]}"); // Assert Assert.Equal("Hello", obj.Name); Assert.Equal(1, obj.Age); Assert.Equal(3, obj.Grades.Length); Assert.Equal("A", obj.Grades[0]); Assert.Equal("B", obj.Grades[1]); Assert.Equal("C", obj.Grades[2]); } [Fact] public void DecodeDynamicObjectImplicitConversionToDictionary() { // Act IDictionary values = Json.Decode("{\"Name\":\"Hello\",\"Age\":1}"); // Assert Assert.Equal("Hello", values["Name"]); Assert.Equal(1, values["Age"]); } [Fact] public void DecodeArrayImplicitConversionToArrayAndObjectArray() { // Act Array array = Json.Decode("[1,2,3]"); object[] objArray = Json.Decode("[1,2,3]"); IEnumerable dynamicEnumerable = Json.Decode("[{a:1}]"); // Assert Assert.NotNull(array); Assert.NotNull(objArray); Assert.NotNull(dynamicEnumerable); } [Fact] public void DecodeArrayImplicitConversionToArrayArrayValuesAreDynamic() { // Act dynamic[] objArray = Json.Decode("[{\"A\":1}]"); // Assert Assert.NotNull(objArray); Assert.Equal(1, objArray[0].A); } [Fact] public void DecodeDynamicObjectAccessPropertiesByIndexer() { // Arrange var obj = Json.Decode("{\"Name\":\"Hello\",\"Age\":1,\"Grades\":[\"A\",\"B\",\"C\"]}"); // Assert Assert.Equal("Hello", obj["Name"]); Assert.Equal(1, obj["Age"]); Assert.Equal(3, obj["Grades"].Length); Assert.Equal("A", obj["Grades"][0]); Assert.Equal("B", obj["Grades"][1]); Assert.Equal("C", obj["Grades"][2]); } [Fact] public void DecodeDynamicObjectAccessPropertiesByNullIndexerReturnsNull() { // Arrange var obj = Json.Decode("{\"Name\":\"Hello\",\"Age\":1,\"Grades\":[\"A\",\"B\",\"C\"]}"); // Assert Assert.Null(obj[null]); } [Fact] public void DecodeDateTime() { // Act DateTime dateTime = Json.Decode("\"\\/Date(940402800000)\\/\""); // Assert Assert.Equal(1999, dateTime.Year); Assert.Equal(10, dateTime.Month); Assert.Equal(20, dateTime.Day); } [Fact] public void DecodeNumber() { // Act int number = Json.Decode("1"); // Assert Assert.Equal(1, number); } [Fact] public void DecodeString() { // Act string @string = Json.Decode("\"1\""); // Assert Assert.Equal("1", @string); } [Fact] public void DecodeArray() { // Act var values = Json.Decode("[11,12,13,14,15]"); // Assert Assert.Equal(5, values.Length); Assert.Equal(11, values[0]); Assert.Equal(12, values[1]); Assert.Equal(13, values[2]); Assert.Equal(14, values[3]); Assert.Equal(15, values[4]); } [Fact] public void DecodeObjectWithArrayProperty() { // Act var obj = Json.Decode("{\"A\":1,\"B\":[1,3,4]}"); object[] bValues = obj.B; // Assert Assert.Equal(1, obj.A); Assert.Equal(1, bValues[0]); Assert.Equal(3, bValues[1]); Assert.Equal(4, bValues[2]); } [Fact] public void DecodeArrayWithObjectValues() { // Act var obj = Json.Decode("[{\"A\":1},{\"B\":3, \"C\": \"hello\"}]"); // Assert Assert.Equal(2, obj.Length); Assert.Equal(1, obj[0].A); Assert.Equal(3, obj[1].B); Assert.Equal("hello", obj[1].C); } [Fact] public void DecodeArraySetValues() { // Arrange var values = Json.Decode("[1,2,3,4,5]"); for (int i = 0; i < values.Length; i++) { values[i]++; } // Assert Assert.Equal(5, values.Length); Assert.Equal(2, values[0]); Assert.Equal(3, values[1]); Assert.Equal(4, values[2]); Assert.Equal(5, values[3]); Assert.Equal(6, values[4]); } [Fact] public void DecodeArrayPassToMethodThatTakesArray() { // Arrange var values = Json.Decode("[3,2,1]"); // Act int index = Array.IndexOf(values, 2); // Assert Assert.Equal(1, index); } [Fact] public void DecodeArrayGetEnumerator() { // Arrange var values = Json.Decode("[1,2,3]"); // Assert int val = 1; foreach (var value in values) { Assert.Equal(val, val); val++; } } [Fact] public void DecodeObjectPropertyAccessIsSameObjectInstance() { // Arrange var obj = Json.Decode("{\"Name\":{\"Version:\":4.0, \"Key\":\"Key\"}}"); // Assert Assert.Same(obj.Name, obj.Name); } [Fact] public void DecodeArrayAccessingMembersThatDontExistReturnsNull() { // Act var obj = Json.Decode("[\"a\", \"b\"]"); // Assert Assert.Null(obj.PropertyThatDoesNotExist); } [Fact] public void DecodeObjectSetProperties() { // Act var obj = Json.Decode("{\"A\":{\"B\":100}}"); obj.A.B = 20; // Assert Assert.Equal(20, obj.A.B); } [Fact] public void DecodeObjectSettingObjectProperties() { // Act var obj = Json.Decode("{\"A\":1}"); obj.A = new { B = 1, D = 2 }; // Assert Assert.Equal(1, obj.A.B); Assert.Equal(2, obj.A.D); } [Fact] public void DecodeObjectWithArrayPropertyPassPropertyToMethodThatTakesArray() { // Arrange var obj = Json.Decode("{\"A\":[3,2,1]}"); // Act Array.Sort(obj.A); // Assert Assert.Equal(1, obj.A[0]); Assert.Equal(2, obj.A[1]); Assert.Equal(3, obj.A[2]); } [Fact] public void DecodeObjectAccessingMembersThatDontExistReturnsNull() { // Act var obj = Json.Decode("{\"A\":1}"); // Assert Assert.Null(obj.PropertyThatDoesntExist); } [Fact] public void DecodeObjectWithSpecificType() { // Act var person = Json.Decode("{\"Name\":\"David\", \"Age\":2}"); // Assert Assert.Equal("David", person.Name); Assert.Equal(2, person.Age); } [Fact] public void DecodeObjectWithImplicitConversionToNonDynamicTypeThrows() { // Act & Assert Assert.Throws(() => { Person person = Json.Decode("{\"Name\":\"David\", \"Age\":2, \"Address\":{\"Street\":\"Bellevue\"}}"); }, "Unable to convert to \"System.Web.Helpers.Test.JsonTest+Person\". Use Json.Decode instead."); } private class DummyDynamicObject : DynamicObject { private IDictionary _values = new Dictionary(); public override IEnumerable GetDynamicMemberNames() { return _values.Keys; } public override bool TrySetMember(SetMemberBinder binder, object value) { _values[binder.Name] = value; return true; } public override bool TryGetMember(GetMemberBinder binder, out object result) { return _values.TryGetValue(binder.Name, out result); } } private class Person { public string Name { get; set; } public int Age { get; set; } public int GPA { get; set; } public Address Address { get; set; } } private class Address { public string Street { get; set; } } } } ================================================ FILE: test/System.Web.Helpers.Test/ObjectInfoTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Dynamic; using System.Linq; using System.Web.TestUtil; using Microsoft.TestCommon; namespace System.Web.Helpers.Test { public class ObjectInfoTest { [Fact] public void PrintWithNegativeDepthThrows() { // Act & Assert Assert.ThrowsArgumentGreaterThanOrEqualTo(() => ObjectInfo.Print(null, depth: -1), "depth", "0"); } [Fact] public void PrintWithInvalidEnumerationLength() { // Act & Assert Assert.ThrowsArgumentGreaterThan(() => ObjectInfo.Print(null, enumerationLength: -1), "enumerationLength", "0"); } [Fact] public void PrintWithNull() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); // Act visitor.Print(null); // Assert string value = Assert.Single(visitor.Values); Assert.Equal("null", value); } [Fact] public void PrintWithEmptyString() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); // Act visitor.Print(String.Empty); // Assert string value = Assert.Single(visitor.Values); Assert.Equal(String.Empty, value); } [Fact] public void PrintWithInt() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); // Act visitor.Print(404); // Assert string value = Assert.Single(visitor.Values); Assert.Equal("404", value); } [Fact] public void PrintWithIDictionary() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); IDictionary dict = new OrderedDictionary(); dict.Add("foo", "bar"); dict.Add("abc", 500); // Act visitor.Print(dict); // Assert Assert.Equal("foo = bar", visitor.KeyValuePairs[0]); Assert.Equal("abc = 500", visitor.KeyValuePairs[1]); } [Fact] public void PrintWithIEnumerable() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var values = Enumerable.Range(0, 10); // Act visitor.Print(values); // Assert foreach (var num in values) { Assert.Contains(num.ToString(), visitor.Values); } } [Fact] public void PrintWithGenericIListPrintsIndex() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var values = Enumerable.Range(0, 10).ToList(); // Act visitor.Print(values); // Assert for (int i = 0; i < values.Count; i++) { Assert.Contains(values[i].ToString(), visitor.Values); Assert.Contains(i, visitor.Indexes); } } [Fact] public void PrintWithArrayPrintsIndex() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var values = Enumerable.Range(0, 10).ToArray(); // Act visitor.Print(values); // Assert for (int i = 0; i < values.Length; i++) { Assert.Contains(values[i].ToString(), visitor.Values); Assert.Contains(i, visitor.Indexes); } } [Fact] public void PrintNameValueCollectionPrintsKeysAndValues() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var values = new NameValueCollection(); values["a"] = "1"; values["b"] = null; // Act visitor.Print(values); // Assert Assert.Equal("a = 1", visitor.KeyValuePairs[0]); Assert.Equal("b = null", visitor.KeyValuePairs[1]); } [Fact] public void PrintDateTime() { using (new CultureReplacer("en-US")) { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var dt = new DateTime(2001, 11, 20, 10, 30, 1); // Act visitor.Print(dt); // Assert Assert.Equal("11/20/2001 10:30:01 AM", visitor.Values[0]); } } [Fact] public void PrintCustomObjectPrintsMembers() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var person = new Person { Name = "David", Age = 23.3, Dob = new DateTime(1986, 11, 19), LongType = 1000000000, Type = 1 }; using (new CultureReplacer("en-US")) { // Act visitor.Print(person); // Assert Assert.Equal(9, visitor.Members.Count); Assert.Contains("double Age = 23.3", visitor.Members); Assert.Contains("string Name = David", visitor.Members); Assert.Contains("DateTime Dob = 11/19/1986 12:00:00 AM", visitor.Members); Assert.Contains("short Type = 1", visitor.Members); Assert.Contains("float Float = 0", visitor.Members); Assert.Contains("byte Byte = 0", visitor.Members); Assert.Contains("decimal Decimal = 0", visitor.Members); Assert.Contains("bool Bool = False", visitor.Members); } } [Fact] public void PrintShowsVisitedWhenCircularReferenceInObjectGraph() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); PersonNode node = new PersonNode { Person = new Person { Name = "David", Age = 23.3 } }; node.Next = node; // Act visitor.Print(node); // Assert Assert.Contains("string Name = David", visitor.Members); Assert.Contains(String.Format("double Age = {0}", 23.3), visitor.Members); Assert.Contains("PersonNode Next = Visited", visitor.Members); } [Fact] public void PrintShowsVisitedWhenCircularReferenceIsIEnumerable() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); List values = new List(); values.Add(values); // Act visitor.Print(values); // Assert Assert.Equal("Visited", visitor.Values[0]); Assert.Equal("Visited " + values.GetHashCode(), visitor.Visited[0]); } [Fact] public void PrintShowsVisitedWhenCircularReferenceIsIDictionary() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); OrderedDictionary values = new OrderedDictionary(); values[values] = values; // Act visitor.Print(values); // Assert Assert.Equal("Visited", visitor.Values[0]); Assert.Equal("Visited " + values.GetHashCode(), visitor.Visited[0]); } [Fact] public void PrintShowsVisitedWhenCircularReferenceIsNameValueCollection() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); NameValueCollection nameValues = new NameValueCollection(); nameValues["id"] = "1"; List values = new List(); values.Add(nameValues); values.Add(nameValues); // Act visitor.Print(values); // Assert Assert.Contains("Visited", visitor.Values); Assert.Contains("Visited " + nameValues.GetHashCode(), visitor.Visited); } [Fact] public void PrintExcludesWriteOnlyProperties() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); ClassWithWriteOnlyProperty cls = new ClassWithWriteOnlyProperty(); // Act visitor.Print(cls); // Assert Assert.Empty(visitor.Members); } [Fact] public void PrintWritesEnumeratedElementsUntilLimitIsReached() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var enumeration = Enumerable.Range(0, 2000); // Act visitor.Print(enumeration); // Assert for (int i = 0; i <= 2000; i++) { if (i < 1000) { Assert.Contains(i.ToString(), visitor.Values); } else { Assert.DoesNotContain(i.ToString(), visitor.Values); } } Assert.Contains("Limit Exceeded", visitor.Values); } [Fact] public void PrintWithAnonymousType() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); var value = new { Name = "John", X = 1 }; // Act visitor.Print(value); // Assert Assert.Contains("string Name = John", visitor.Members); Assert.Contains("int X = 1", visitor.Members); } [Fact] public void PrintClassWithPublicFields() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); ClassWithFields value = new ClassWithFields(); value.Foo = "John"; value.Bar = 1; // Actt visitor.Print(value); // Assert Assert.Contains("string Foo = John", visitor.Members); Assert.Contains("int Bar = 1", visitor.Members); } [Fact] public void PrintClassWithDynamicMembersPrintsMembersIfGetDynamicMemberNamesIsImplemented() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); dynamic d = new DynamicDictionary(); d.Cycle = d; d.Name = "Foo"; d.Value = null; // Act visitor.Print(d); // Assert Assert.Contains("DynamicDictionary Cycle = Visited", visitor.Members); Assert.Contains("string Name = Foo", visitor.Members); Assert.Contains("Value = null", visitor.Members); } [Fact] public void PrintClassWithDynamicMembersReturningNullPrintsNoMembers() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); dynamic d = new ClassWithDynamicAnNullMemberNames(); d.Cycle = d; d.Name = "Foo"; d.Value = null; // Act visitor.Print(d); // Assert Assert.False(visitor.Members.Any()); } [Fact] public void PrintUsesToStringOfIConvertibleObjects() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); ConvertibleClass cls = new ConvertibleClass(); // Act visitor.Print(cls); // Assert Assert.Equal("Test", visitor.Values[0]); } [Fact] public void PrintConvertsTypeToString() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); // Act visitor.Print(typeof(string)); // Assert Assert.Equal("typeof(string)", visitor.Values[0]); } [Fact] [ReplaceCulture] public void PrintClassWithPropertyThatThrowsExceptionPrintsException() { // Arrange MockObjectVisitor visitor = CreateObjectVisitor(); ClassWithPropertyThatThrowsException value = new ClassWithPropertyThatThrowsException(); // Act visitor.Print(value); // Assert Assert.Equal("int MyProperty = Property accessor 'MyProperty' on object 'System.Web.Helpers.Test.ObjectInfoTest+ClassWithPropertyThatThrowsException' threw the following exception:'Property that shows an exception'", visitor.Members[0]); } [Fact] public void ConvertEscapeSequencesPrintsStringEscapeSequencesAsLiterals() { // Act string value = HtmlObjectPrinter.ConvertEscapseSequences("\\\'\"\0\a\b\f\n\r\t\v"); // Assert Assert.Equal("\\\\'\\\"\\0\\a\\b\\f\\n\\r\\t\\v", value); } [Fact] public void ConvertEscapeSequencesDoesNotEscapeUnicodeSequences() { // Act string value = HtmlObjectPrinter.ConvertEscapseSequences("\u1023\x2045"); // Assert Assert.Equal("\u1023\x2045", value); } [Fact] public void PrintCharPrintsQuotedString() { // Arrange HtmlObjectPrinter printer = new HtmlObjectPrinter(100, 100); HtmlElement element = new HtmlElement("span"); printer.PushElement(element); // Act printer.VisitConvertedValue('x', "x"); // Assert Assert.Equal(1, element.Children.Count); HtmlElement child = element.Children[0]; Assert.Equal("'x'", child.InnerText); Assert.Equal("quote", child["class"]); } [Fact] public void PrintEscapeCharPrintsEscapedCharAsLiteral() { // Arrange HtmlObjectPrinter printer = new HtmlObjectPrinter(100, 100); HtmlElement element = new HtmlElement("span"); printer.PushElement(element); // Act printer.VisitConvertedValue('\t', "\t"); // Assert Assert.Equal(1, element.Children.Count); HtmlElement child = element.Children[0]; Assert.Equal("'\\t'", child.InnerText); Assert.Equal("quote", child["class"]); } [Fact] public void GetTypeNameConvertsGenericTypesToCsharpSyntax() { // Act string value = ObjectVisitor.GetTypeName(typeof(Func, Action>>)); // Assert Assert.Equal("Func, Action>>", value); } private class ConvertibleClass : IConvertible { public TypeCode GetTypeCode() { throw new NotImplementedException(); } public bool ToBoolean(IFormatProvider provider) { throw new NotImplementedException(); } public byte ToByte(IFormatProvider provider) { throw new NotImplementedException(); } public char ToChar(IFormatProvider provider) { throw new NotImplementedException(); } public DateTime ToDateTime(IFormatProvider provider) { throw new NotImplementedException(); } public decimal ToDecimal(IFormatProvider provider) { throw new NotImplementedException(); } public double ToDouble(IFormatProvider provider) { throw new NotImplementedException(); } public short ToInt16(IFormatProvider provider) { throw new NotImplementedException(); } public int ToInt32(IFormatProvider provider) { throw new NotImplementedException(); } public long ToInt64(IFormatProvider provider) { throw new NotImplementedException(); } public sbyte ToSByte(IFormatProvider provider) { throw new NotImplementedException(); } public float ToSingle(IFormatProvider provider) { throw new NotImplementedException(); } public string ToString(IFormatProvider provider) { return "Test"; } public object ToType(Type conversionType, IFormatProvider provider) { throw new NotImplementedException(); } public ushort ToUInt16(IFormatProvider provider) { throw new NotImplementedException(); } public uint ToUInt32(IFormatProvider provider) { throw new NotImplementedException(); } public ulong ToUInt64(IFormatProvider provider) { throw new NotImplementedException(); } } private class ClassWithPropertyThatThrowsException { public int MyProperty { get { throw new InvalidOperationException("Property that shows an exception"); } } } private class ClassWithDynamicAnNullMemberNames : DynamicObject { public override IEnumerable GetDynamicMemberNames() { return null; } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { return true; } } private class Person { public string Name { get; set; } public double Age { get; set; } public DateTime Dob { get; set; } public short Type { get; set; } public long LongType { get; set; } public float Float { get; set; } public byte Byte { get; set; } public decimal Decimal { get; set; } public bool Bool { get; set; } } private class ClassWithFields { public string Foo; public int Bar = 13; } private class ClassWithWriteOnlyProperty { public int Value { set { } } } private class PersonNode { public Person Person { get; set; } public PersonNode Next { get; set; } } private MockObjectVisitor CreateObjectVisitor(int recursionLimit = 10, int enumerationLimit = 1000) { return new MockObjectVisitor(recursionLimit, enumerationLimit); } private class MockObjectVisitor : ObjectVisitor { public MockObjectVisitor(int recursionLimit, int enumerationLimit) : base(recursionLimit, enumerationLimit) { Values = new List(); KeyValuePairs = new List(); Members = new List(); Indexes = new List(); Visited = new List(); } public List Values { get; set; } public List KeyValuePairs { get; set; } public List Members { get; set; } public List Indexes { get; set; } public List Visited { get; set; } public void Print(object value) { Visit(value, 0); } public override void VisitObjectVisitorException(ObjectVisitorException exception) { Values.Add(exception.InnerException.Message); } public override void VisitStringValue(string stringValue) { Values.Add(stringValue); base.VisitStringValue(stringValue); } public override void VisitVisitedObject(string id, object value) { Visited.Add(String.Format("Visited {0}", id)); Values.Add("Visited"); base.VisitVisitedObject(id, value); } public override void VisitIndexedEnumeratedValue(int index, object item, int depth) { Indexes.Add(index); base.VisitIndexedEnumeratedValue(index, item, depth); } public override void VisitEnumeratonLimitExceeded() { Values.Add("Limit Exceeded"); base.VisitEnumeratonLimitExceeded(); } public override void VisitMember(string name, Type type, object value, int depth) { base.VisitMember(name, type, value, depth); type = type ?? (value != null ? value.GetType() : null); if (type == null) { Members.Add(String.Format("{0} = null", name)); } else { Members.Add(String.Format("{0} {1} = {2}", GetTypeName(type), name, Values.Last())); } } public override void VisitNull() { Values.Add("null"); base.VisitNull(); } public override void VisitKeyValue(object key, object value, int depth) { base.VisitKeyValue(key, value, depth); KeyValuePairs.Add(String.Format("{0} = {1}", Values[Values.Count - 2], Values[Values.Count - 1])); } } } } ================================================ FILE: test/System.Web.Helpers.Test/PreComputedGridDataSourceTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using Microsoft.TestCommon; using Moq; namespace System.Web.Helpers.Test { public class PreComputedGridDataSourceTest { [Fact] public void PreSortedDataSourceReturnsRowCountItWasSpecified() { // Arrange int rows = 20; var dataSource = new PreComputedGridDataSource(new WebGrid(GetContext()), values: Enumerable.Range(0, 10).Cast(), totalRows: rows); // Act and Assert Assert.Equal(rows, dataSource.TotalRowCount); } [Fact] public void PreSortedDataSourceReturnsAllRows() { // Arrange var grid = new WebGrid(GetContext()); var dataSource = new PreComputedGridDataSource(grid: grid, values: Enumerable.Range(0, 10).Cast(), totalRows: 10); // Act var rows = dataSource.GetRows(new SortInfo { SortColumn = String.Empty }, 0); // Assert Assert.Equal(10, rows.Count); Assert.Equal(0, rows.First().Value); Assert.Equal(9, rows.Last().Value); } private HttpContextBase GetContext() { return new Mock().Object; } } } ================================================ FILE: test/System.Web.Helpers.Test/ServerInfoTest.cs ================================================ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Collections.Specialized; using System.Web.WebPages.TestUtils; using Microsoft.TestCommon; using Moq; namespace System.Web.Helpers.Test { public class InfoTest { [Fact] public void ConfigurationReturnsExpectedInfo() { var configInfo = ServerInfo.Configuration(); // verification // checks only subset of values Assert.NotNull(configInfo); VerifyKey(configInfo, "Machine Name"); VerifyKey(configInfo, "OS Version"); VerifyKey(configInfo, "ASP.NET Version"); VerifyKey(configInfo, "ASP.NET Web Pages Version"); } [Fact] public void EnvironmentVariablesReturnsExpectedInfo() { var envVariables = ServerInfo.EnvironmentVariables(); // verification // checks only subset of values Assert.NotNull(envVariables); VerifyKey(envVariables, "Path"); VerifyKey(envVariables, "SystemDrive"); } [Fact] public void ServerVariablesReturnsExpectedInfoWithNoContext() { var serverVariables = ServerInfo.ServerVariables(); // verification // since there is no HttpContext this will be empty Assert.NotNull(serverVariables); } [Fact] public void ServerVariablesReturnsExpectedInfoWthContext() { var serverVariables = new NameValueCollection(); serverVariables.Add("foo", "bar"); var request = new Mock(); request.Setup(c => c.ServerVariables).Returns(serverVariables); var context = new Mock(); context.Setup(c => c.Request).Returns(request.Object); // verification Assert.NotNull(serverVariables); IDictionary returnedValues = ServerInfo.ServerVariables(context.Object); Assert.Equal(serverVariables.Count, returnedValues.Count); foreach (var item in returnedValues) { Assert.Equal(serverVariables[item.Key], item.Value); } } #if false // Avoid an NRE when accessing HttpRuntime.BinDirectory without initializing the runtime. [Fact] public void HttpRuntimeInfoReturnsExpectedInfo() { var httpRuntimeInfo = ServerInfo.HttpRuntimeInfo(); // verification // checks only subset of values Assert.NotNull(httpRuntimeInfo); VerifyKey(httpRuntimeInfo, "CLR Install Directory"); VerifyKey(httpRuntimeInfo, "Asp Install Directory"); VerifyKey(httpRuntimeInfo, "On UNC Share"); } #endif [Fact] public void ServerInfoDoesNotProduceLegacyCasForHomogenousAppDomain() { // Act and Assert Action action = () => { IDictionary configValue = ServerInfo.LegacyCAS(AppDomain.CurrentDomain); Assert.NotNull(configValue); Assert.Equal(0, configValue.Count); }; AppDomainUtils.RunInSeparateAppDomain(GetAppDomainSetup(legacyCasEnabled: false), action); } [Fact] public void ServerInfoProducesLegacyCasForNonHomogenousAppDomain() { // Arrange Action action = () => { // Act and Assert IDictionary configValue = ServerInfo.LegacyCAS(AppDomain.CurrentDomain); // Assert Assert.True(configValue.ContainsKey("Legacy Code Access Security")); Assert.Equal("Legacy Code Access Security has been detected on your system. Microsoft WebPage features require the ASP.NET 4 Code Access Security model. For information about how to resolve this, contact your server administrator.", configValue["Legacy Code Access Security"]); }; AppDomainUtils.RunInSeparateAppDomain(GetAppDomainSetup(legacyCasEnabled: true), action); } //[Fact] //public void SqlServerInfoReturnsExpectedInfo() { // var sqlInfo = ServerInfo.SqlServerInfo(); // // verification // // just verifies that we don't get any unexpected exceptions // Assert.NotNull(sqlInfo); //} #if false // Avoid NREs when accessing HttpRuntime.BinDirectory without initializing the runtime. [Fact] public void RenderResultContainsExpectedTags() { var htmlString = ServerInfo.GetHtml().ToString(); // just verify that the final HTML produced contains some expected info Assert.True(htmlString.Contains("")); Assert.True(htmlString.Contains("")); Assert.True(htmlString.Contains("Server Configuration")); } [Fact] public void RenderGeneratesValidXhtml() { // Result does not validate against XHTML 1.1 and HTML5 because ServerInfo generates //

MICROSOFT SOFTWARE LICENSE TERMS

MICROSOFT .NET LIBRARY

These license terms are an agreement between you and Microsoft Corporation (or based on where you live, one of its affiliates). They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms.

If you comply with these license terms, you have the rights below.

1.    INSTALLATION AND USE RIGHTS.

You may install and use any number of copies of the software to develop and test your applications. 

2.    THIRD PARTY COMPONENTS. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software.

3.    ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.

a.     DISTRIBUTABLE CODE.  The software is comprised of Distributable Code. Distributable Code is code that you are permitted to distribute in applications you develop if you comply with the terms below.

i.      Right to Use and Distribute.

        You may copy and distribute the object code form of the software.

        Third Party Distribution. You may permit distributors of your applications to copy and distribute the Distributable Code as part of those applications.

ii.     Distribution Requirements. For any Distributable Code you distribute, you must

        use the Distributable Code in your applications and not as a standalone distribution;

        require distributors and external end users to agree to terms that protect it at least as much as this agreement; and

        indemnify, defend, and hold harmless Microsoft from any claims, including attorneys fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the unmodified Distributable Code.

iii.   Distribution Restrictions. You may not

        use Microsofts trademarks in your applications names or in a way that suggests your applications come from or are endorsed by Microsoft; or

        modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution of code, that (i) it be disclosed or distributed in source code form; or (ii) others have the right to modify it.

4.    DATA.

a.     Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services.  You may opt-out of many of these scenarios, but not all, as described in the software documentation.  There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with Microsofts privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and its use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices.

b.    Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr.

5.    Scope of License. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not

        work around any technical limitations in the software;

        reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included in the software;

        remove, minimize, block or modify any notices of Microsoft or its suppliers in the software;

        use the software in any way that is against the law; or

        share, publish, rent or lease the software, provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party.

6.    Export Restrictions. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting.  

7.    SUPPORT SERVICES. Because this software is as is, we may not provide support services for it.

8.    Entire Agreement. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.

9.    Applicable LawIf you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply.

10. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you:

a)    Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights.

b)    Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software.

c)    Germany and Austria.

(i)        Warranty. The software will perform substantially as described in any Microsoft materials that accompany it. However, Microsoft gives no contractual guarantee in relation to the software.

(ii)       Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as in case of death or personal or physical injury, Microsoft is liable according to the statutory law.

Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence

11. Disclaimer of Warranty. THE SOFTWARE IS LICENSED AS-IS. YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.

12. Limitation on and Exclusion of Remedies and Damages. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.

This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.

It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your state or country may not allow the exclusion or limitation of incidental, consequential or other damages.

 

================================================ FILE: tools/vslicense/dotnet-library-license.md ================================================ **MICROSOFT SOFTWARE LICENSE TERMS** **MICROSOFT .NET LIBRARY** ------------------------------------------------------------------------------ These license terms are an agreement between you and Microsoft Corporation (or based on where you live, one of its affiliates). They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. ------------------------------------------------------------------------------ **IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW.** **1. INSTALLATION AND USE RIGHTS.** > You may install and use any number of copies of the software to develop and test your applications. **2. THIRD PARTY COMPONENTS.** The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. **3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.** > **a. DISTRIBUTABLE CODE.** The software is comprised of Distributable Code. "Distributable Code" is code that you are permitted to distribute in applications you develop if you comply with the terms below. >> **i. Right to Use and Distribute.** >> - You may copy and distribute the object code form of the software. >> - Third Party Distribution. You may permit distributors of your applications to copy and distribute the Distributable Code as part of those applications. >> **ii. Distribution Requirements. For any Distributable Code you distribute, you must** >> - use the Distributable Code in your applications and not as a standalone distribution; >> - require distributors and external end users to agree to terms that protect it at least as much as this agreement; and >> - indemnify, defend, and hold harmless Microsoft from any claims, including attorneys' fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the unmodified Distributable Code. >> **iii. Distribution Restrictions. You may not** >> - use Microsoft's trademarks in your applications' names or in a way that suggests your applications come from or are endorsed by Microsoft; or >> - modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An "Excluded License" is one that requires, as a condition of use, modification or distribution of code, that (i) it be disclosed or distributed in source code form; or (ii) others have the right to modify it. **4. DATA.** > **a. Data Collection.** The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the software documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with Microsoft's privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and its use from the software documentation and our privacy statement. Your use of the software operates as your consent to these practices. > **b. Processing of Personal Data.** To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr. **5. SCOPE OF LICENSE.** The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not > - work around any technical limitations in the software; > - reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party licensing terms governing use of certain open source components that may be included in the software; > - remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; > - use the software in any way that is against the law; or > - share, publish, rent or lease the software, provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party. **6. EXPORT RESTRICTIONS.** You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit www.microsoft.com/exporting. **7. SUPPORT SERVICES.** Because this software is "as is", we may not provide support services for it. **8. ENTIRE AGREEMENT.** This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. **9. APPLICABLE LAW.** If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. **10. CONSUMER RIGHTS; REGIONAL VARIATIONS.** This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: > **a) Australia.** You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. > **b) Canada.** If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. > **c) Germany and Austria.** >> **(i) Warranty.** The software will perform substantially as described in any Microsoft materials that accompany it. However, Microsoft gives no contractual guarantee in relation to the software. >> **(ii) Limitation of Liability.** In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as in case of death or personal or physical injury, Microsoft is liable according to the statutory law. > Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence **11. Disclaimer of Warranty. THE SOFTWARE IS LICENSED "AS-IS". YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.** **12. Limitation on and Exclusion of Remedies and Damages. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.** This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your state or country may not allow the exclusion or limitation of incidental, consequential or other damages.