Repository: NancyFx/Nancy Branch: master Commit: e523defb12f5 Files: 1321 Total size: 4.7 MB Directory structure: gitextract_xv9v7nbg/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE.md │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .mailmap ├── .travis.yml ├── Nancy.ruleset ├── Nancy.sln ├── Nancy.sln.DotSettings ├── NuGet.config ├── README.md ├── SharedAssemblyInfo.cs ├── appveyor.yml ├── build.cake ├── build.ps1 ├── build.sh ├── favicon.license.txt ├── global.json ├── how_to_build.txt ├── license.txt ├── samples/ │ ├── Nancy.Demo.Async/ │ │ ├── App.config │ │ ├── MainModule.cs │ │ ├── Nancy.Demo.Async.csproj │ │ └── Program.cs │ ├── Nancy.Demo.Authentication/ │ │ ├── AnotherVerySecureModule.cs │ │ ├── AuthenticationBootstrapper.cs │ │ ├── MainModule.cs │ │ ├── Models/ │ │ │ └── UserModel.cs │ │ ├── Nancy.Demo.Authentication.csproj │ │ ├── README.txt │ │ ├── SecureModule.cs │ │ ├── Views/ │ │ │ ├── Index.cshtml │ │ │ ├── Login.cshtml │ │ │ ├── secure.cshtml │ │ │ └── superSecure.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Authentication.Basic/ │ │ ├── AuthenticationBootstrapper.cs │ │ ├── MainModule.cs │ │ ├── Nancy.Demo.Authentication.Basic.csproj │ │ ├── SecureModule.cs │ │ ├── UserValidator.cs │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Authentication.Forms/ │ │ ├── FormsAuthBootstrapper.cs │ │ ├── MainModule.cs │ │ ├── Models/ │ │ │ └── UserModel.cs │ │ ├── Nancy.Demo.Authentication.Forms.csproj │ │ ├── PartlySecureModule.cs │ │ ├── README.txt │ │ ├── SecureModule.cs │ │ ├── UserDatabase.cs │ │ ├── Views/ │ │ │ ├── index.cshtml │ │ │ ├── login.cshtml │ │ │ └── secure.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Authentication.Forms.TestingDemo/ │ │ ├── LoginFixture.cs │ │ ├── Nancy.Demo.Authentication.Forms.TestingDemo.csproj │ │ ├── TestBootstrapper.cs │ │ ├── TestRootPathProvider.cs │ │ └── packages.config │ ├── Nancy.Demo.Authentication.Stateless/ │ │ ├── AuthModule.cs │ │ ├── Models/ │ │ │ └── UserModel.cs │ │ ├── Nancy.Demo.Authentication.Stateless.csproj │ │ ├── RootModule.cs │ │ ├── SecureModule.cs │ │ ├── StatelessAuthBootstrapper.cs │ │ ├── UserDatabase.cs │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Authentication.Stateless.Website/ │ │ ├── Nancy.Demo.Authentication.Stateless.Website.csproj │ │ ├── Scripts/ │ │ │ ├── api.js │ │ │ └── apiToken.js │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ ├── index.html │ │ ├── login.html │ │ └── secure.html │ ├── Nancy.Demo.Bootstrapper.Aspnet/ │ │ ├── ApplicationDependencyClass.cs │ │ ├── Bootstrapper.cs │ │ ├── DependencyModule.cs │ │ ├── IApplicationDependency.cs │ │ ├── IRequestDependency.cs │ │ ├── Models/ │ │ │ ├── RatPack.cs │ │ │ └── RatPackWithDependencyText.cs │ │ ├── Nancy.Demo.Bootstrapping.Aspnet.csproj │ │ ├── README.txt │ │ ├── RequestDependencyClass.cs │ │ ├── Views/ │ │ │ └── razor-dependency.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Caching/ │ │ ├── CachedResponse.cs │ │ ├── CachingBootstrapper.cs │ │ ├── CachingExtensions/ │ │ │ └── ContextExtensions.cs │ │ ├── MainModule.cs │ │ ├── Nancy.Demo.Caching.csproj │ │ ├── README.txt │ │ ├── Views/ │ │ │ ├── Index.cshtml │ │ │ └── Payload.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.ConstraintRouting/ │ │ ├── ConstraintRoutingModule.cs │ │ ├── EmailRouteSegmentConstraint.cs │ │ ├── Nancy.Demo.ConstraintRouting.csproj │ │ ├── Views/ │ │ │ └── Index.html │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.CustomModule/ │ │ ├── DemoBootstrapper.cs │ │ ├── MainModule.cs │ │ ├── Nancy.Demo.CustomModule.csproj │ │ ├── NancyRouteAttribute.cs │ │ ├── UglifiedNancyModule.cs │ │ ├── Views/ │ │ │ └── Index.html │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.Hosting.Aspnet/ │ │ ├── ApplicationDependencyClass.cs │ │ ├── Content/ │ │ │ ├── main.css │ │ │ └── scripts.js │ │ ├── CustomStatusHandler.cs │ │ ├── DefaultRouteMetadataProvider.cs │ │ ├── DemoBootstrapper.cs │ │ ├── DependencyModule.cs │ │ ├── HereBeAResponseYouScurvyDog.cs │ │ ├── IApplicationDependency.cs │ │ ├── IRequestDependency.cs │ │ ├── MainModule.cs │ │ ├── Metadata/ │ │ │ ├── MainMetadataModule.cs │ │ │ └── MyUberRouteMetadata.cs │ │ ├── Models/ │ │ │ ├── Payload.cs │ │ │ ├── RatPack.cs │ │ │ ├── RatPackWithDependencyText.cs │ │ │ ├── Razor2.cs │ │ │ └── SomeViewModel.cs │ │ ├── MyConfig.cs │ │ ├── MyConfigExtensions.cs │ │ ├── Nancy.Demo.Hosting.Aspnet.csproj │ │ ├── Piratizer4000.cs │ │ ├── PngSerializer.cs │ │ ├── README.txt │ │ ├── RequestDependencyClass.cs │ │ ├── Resources/ │ │ │ ├── Menu.Designer.cs │ │ │ └── Menu.resx │ │ ├── Views/ │ │ │ ├── FileUpload.sshtml │ │ │ ├── anon.spark │ │ │ ├── csrf.cshtml │ │ │ ├── dot.liquid │ │ │ ├── interactive-diags-methods.sshtml │ │ │ ├── interactive-diags-results.sshtml │ │ │ ├── interactive-diags.sshtml │ │ │ ├── javascript.html │ │ │ ├── meta.cshtml │ │ │ ├── negotiatedview.cshtml │ │ │ ├── nustache.nustache │ │ │ ├── nustachePartial.nustache │ │ │ ├── razor-dependency.cshtml │ │ │ ├── razor-divzero.cshtml │ │ │ ├── razor-error.cshtml │ │ │ ├── razor-layout-error.cshtml │ │ │ ├── razor-layout.cshtml │ │ │ ├── razor-simple.cshtml │ │ │ ├── razor-strong.cshtml │ │ │ ├── razor-strong.vbhtml │ │ │ ├── razor.cshtml │ │ │ ├── razor2.cshtml │ │ │ ├── routes.cshtml │ │ │ ├── someview.cshtml │ │ │ ├── spark.spark │ │ │ ├── ssve.sshtml │ │ │ ├── static.html │ │ │ └── uber-meta.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ └── packages.config │ ├── Nancy.Demo.Hosting.Kestrel/ │ │ ├── AppConfiguration.cs │ │ ├── DemoBootstrapper.cs │ │ ├── HomeModule.cs │ │ ├── IAppConfiguration.cs │ │ ├── Nancy.Demo.Hosting.Kestrel.csproj │ │ ├── Person.cs │ │ ├── PersonValidator.cs │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── launchSettings.json │ │ ├── Startup.cs │ │ └── appsettings.json │ ├── Nancy.Demo.Hosting.Owin/ │ │ ├── MainModule.cs │ │ ├── Models/ │ │ │ └── Index.cs │ │ ├── Nancy.Demo.Hosting.Owin.csproj │ │ ├── Startup.cs │ │ ├── Views/ │ │ │ └── Root.spark │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ └── packages.config │ ├── Nancy.Demo.Hosting.Self/ │ │ ├── DemoBootstrapper.cs │ │ ├── Models/ │ │ │ └── Index.cs │ │ ├── Nancy.Demo.Hosting.Self.csproj │ │ ├── Program.cs │ │ ├── README.txt │ │ ├── TestModule.cs │ │ ├── Views/ │ │ │ ├── FileUpload.spark │ │ │ └── staticview.html │ │ └── app.config │ ├── Nancy.Demo.MarkdownViewEngine/ │ │ ├── Content/ │ │ │ ├── blog.css │ │ │ └── js/ │ │ │ └── jquery.spritely-0.6.js │ │ ├── Model/ │ │ │ └── BlogModel.cs │ │ ├── Modules/ │ │ │ └── HomeModule.cs │ │ ├── Nancy.Demo.MarkdownViewEngine.csproj │ │ ├── Views/ │ │ │ ├── Posts/ │ │ │ │ ├── future-post.md │ │ │ │ ├── my-first-blog-post.md │ │ │ │ ├── readme.md │ │ │ │ └── why-use-nancy.md │ │ │ ├── blogfooter.html │ │ │ ├── blogheader.html │ │ │ ├── blogindex.html │ │ │ ├── master.html │ │ │ └── popularposts.html │ │ ├── packages.config │ │ └── web.config │ ├── Nancy.Demo.ModelBinding/ │ │ ├── CustomersModule.cs │ │ ├── Database/ │ │ │ └── DB.cs │ │ ├── EventsModule.cs │ │ ├── JsonModule.cs │ │ ├── MainModule.cs │ │ ├── ModelBinders/ │ │ │ └── CustomerModelBinder.cs │ │ ├── ModelBindingBootstrapper.cs │ │ ├── Models/ │ │ │ ├── Customer.cs │ │ │ ├── Event.cs │ │ │ └── User.cs │ │ ├── Nancy.Demo.ModelBinding.csproj │ │ ├── Views/ │ │ │ ├── Customers.spark │ │ │ ├── Events.spark │ │ │ ├── PostJson.html │ │ │ └── PostXml.html │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ └── XmlModule.cs │ ├── Nancy.Demo.Razor.Localization/ │ │ ├── CustomResourceAssemblyProvider.cs │ │ ├── DemoBootstrapper.cs │ │ ├── Modules/ │ │ │ └── HomeModule.cs │ │ ├── Nancy.Demo.Razor.Localization.csproj │ │ ├── Resources/ │ │ │ ├── Text.Designer.cs │ │ │ ├── Text.de-DE.resx │ │ │ ├── Text.en-US.resx │ │ │ └── Text.resx │ │ ├── Views/ │ │ │ ├── CultureView-de-DE.cshtml │ │ │ ├── CultureView.cshtml │ │ │ ├── Index.cshtml │ │ │ └── razor-layout.cshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.SparkViewEngine/ │ │ ├── FifthElement/ │ │ │ ├── Fifth.spark │ │ │ └── FifthElementModule.cs │ │ ├── MainModule.cs │ │ ├── Nancy.Demo.SparkViewEngine.csproj │ │ ├── Views/ │ │ │ ├── Index.spark │ │ │ ├── Main/ │ │ │ │ ├── test.spark │ │ │ │ └── test2.spark │ │ │ ├── Shared/ │ │ │ │ ├── application.spark │ │ │ │ └── html5.spark │ │ │ └── _SmallBit.spark │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ ├── Nancy.Demo.SuperSimpleViewEngine/ │ │ ├── MainModule.cs │ │ ├── Models/ │ │ │ └── MainModel.cs │ │ ├── Nancy.Demo.SuperSimpleViewEngine.csproj │ │ ├── Views/ │ │ │ ├── Index.sshtml │ │ │ ├── Login.sshtml │ │ │ ├── MasterPage.sshtml │ │ │ └── User.sshtml │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ └── Web.config │ └── Nancy.Demo.Validation/ │ ├── CustomersModule.cs │ ├── Database/ │ │ └── DB.cs │ ├── MainModule.cs │ ├── Models/ │ │ ├── Customer.cs │ │ ├── OddLengthStringAttribute.cs │ │ ├── OddLengthStringAttributeAdapter.cs │ │ └── Product.cs │ ├── Nancy.Demo.Validation.csproj │ ├── ProductsModule.cs │ ├── ValidationBootstrapper.cs │ ├── Views/ │ │ ├── CustomerError.spark │ │ └── Customers.spark │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ └── packages.config ├── src/ │ ├── Directory.Build.props │ ├── Nancy/ │ │ ├── AfterPipeline.cs │ │ ├── AppDomainAssemblyCatalog.cs │ │ ├── ArrayCache.cs │ │ ├── AsyncNamedPipelineBase.cs │ │ ├── BeforePipeline.cs │ │ ├── Bootstrapper/ │ │ │ ├── BootstrapperException.cs │ │ │ ├── CollectionTypeRegistration.cs │ │ │ ├── ContainerRegistration.cs │ │ │ ├── FavIconApplicationStartup.cs │ │ │ ├── IApplicationStartup.cs │ │ │ ├── INancyBootstrapper.cs │ │ │ ├── IPipelines.cs │ │ │ ├── IRegistrations.cs │ │ │ ├── IRequestStartup.cs │ │ │ ├── InstanceRegistration.cs │ │ │ ├── Lifetime.cs │ │ │ ├── ModuleRegistrationType.cs │ │ │ ├── MultipleRootPathProvidersLocatedException.cs │ │ │ ├── NancyBootstrapperBase.cs │ │ │ ├── NancyBootstrapperLocator.cs │ │ │ ├── NancyBootstrapperWithRequestContainerBase.cs │ │ │ ├── NancyInternalConfiguration.cs │ │ │ ├── Pipelines.cs │ │ │ ├── Registrations.cs │ │ │ └── TypeRegistration.cs │ │ ├── Configuration/ │ │ │ ├── ConfigurationException.cs │ │ │ ├── DefaultNancyEnvironment.cs │ │ │ ├── DefaultNancyEnvironmentConfigurator.cs │ │ │ ├── DefaultNancyEnvironmentFactory.cs │ │ │ ├── INancyDefaultConfigurationProvider.cs │ │ │ ├── INancyEnvironment.cs │ │ │ ├── INancyEnvironmentConfigurator.cs │ │ │ ├── INancyEnvironmentExtensions.cs │ │ │ ├── INancyEnvironmentFactory.cs │ │ │ └── NancyDefaultConfigurationProvider.cs │ │ ├── Conventions/ │ │ │ ├── AcceptHeaderCoercionConventions.cs │ │ │ ├── BuiltInAcceptHeaderCoercions.cs │ │ │ ├── BuiltInCultureConventions.cs │ │ │ ├── CultureConventions.cs │ │ │ ├── DefaultAcceptHeaderCoercionConventions.cs │ │ │ ├── DefaultCultureConventions.cs │ │ │ ├── DefaultStaticContentsConventions.cs │ │ │ ├── DefaultViewLocationConventions.cs │ │ │ ├── IConvention.cs │ │ │ ├── NancyConventions.cs │ │ │ ├── StaticContentConventionBuilder.cs │ │ │ ├── StaticContentHelper.cs │ │ │ ├── StaticContentsConventions.cs │ │ │ ├── StaticContentsConventionsExtensions.cs │ │ │ ├── StaticDirectoryContent.cs │ │ │ ├── StaticFileContent.cs │ │ │ └── ViewLocationConventions.cs │ │ ├── Cookies/ │ │ │ ├── INancyCookie.cs │ │ │ └── NancyCookie.cs │ │ ├── Cryptography/ │ │ │ ├── AesEncryptionProvider.cs │ │ │ ├── Base64Helpers.cs │ │ │ ├── CryptographyConfiguration.cs │ │ │ ├── DefaultHmacProvider.cs │ │ │ ├── HmacComparer.cs │ │ │ ├── IEncryptionProvider.cs │ │ │ ├── IHmacProvider.cs │ │ │ ├── IKeyGenerator.cs │ │ │ ├── NoEncryptionProvider.cs │ │ │ ├── PassphraseKeyGenerator.cs │ │ │ └── RandomKeyGenerator.cs │ │ ├── Culture/ │ │ │ ├── DefaultCultureService.cs │ │ │ └── ICultureService.cs │ │ ├── DefaultGlobalizationConfigurationProvider.cs │ │ ├── DefaultNancyBootstrapper.cs │ │ ├── DefaultNancyContextFactory.cs │ │ ├── DefaultObjectSerializer.cs │ │ ├── DefaultResponseFormatter.cs │ │ ├── DefaultResponseFormatterFactory.cs │ │ ├── DefaultRootPathProvider.cs │ │ ├── DefaultRouteConfigurationProvider.cs │ │ ├── DefaultRuntimeEnvironmentInformation.cs │ │ ├── DefaultSerializerFactory.cs │ │ ├── DefaultStaticContentConfigurationProvider.cs │ │ ├── DefaultStaticContentProvider.cs │ │ ├── DefaultTraceConfigurationProvider.cs │ │ ├── DefaultTypeCatalog.cs │ │ ├── DefaultViewConfigurationProvider.cs │ │ ├── DependencyContextAssemblyCatalog.cs │ │ ├── Diagnostics/ │ │ │ ├── ConcurrentLimitedCollection.cs │ │ │ ├── DefaultDiagnostics.cs │ │ │ ├── DefaultDiagnosticsConfigurationProvider.cs │ │ │ ├── DefaultRequestTrace.cs │ │ │ ├── DefaultRequestTraceFactory.cs │ │ │ ├── DefaultRequestTracing.cs │ │ │ ├── DefaultTraceLog.cs │ │ │ ├── DescriptionAttribute.cs │ │ │ ├── DiagnosticModule.cs │ │ │ ├── DiagnosticsConfiguration.cs │ │ │ ├── DiagnosticsConfigurationExtensions.cs │ │ │ ├── DiagnosticsHook.cs │ │ │ ├── DiagnosticsModuleBuilder.cs │ │ │ ├── DiagnosticsModuleCatalog.cs │ │ │ ├── DiagnosticsSerializerFactory.cs │ │ │ ├── DiagnosticsSession.cs │ │ │ ├── DiagnosticsViewRenderer.cs │ │ │ ├── DisabledDiagnostics.cs │ │ │ ├── IDiagnostics.cs │ │ │ ├── IDiagnosticsProvider.cs │ │ │ ├── IInteractiveDiagnostics.cs │ │ │ ├── IRequestTrace.cs │ │ │ ├── IRequestTraceFactory.cs │ │ │ ├── IRequestTracing.cs │ │ │ ├── ITraceLog.cs │ │ │ ├── InteractiveDiagnostic.cs │ │ │ ├── InteractiveDiagnosticMethod.cs │ │ │ ├── InteractiveDiagnostics.cs │ │ │ ├── Modules/ │ │ │ │ ├── InfoModule.cs │ │ │ │ ├── InteractiveModule.cs │ │ │ │ ├── MainModule.cs │ │ │ │ ├── SettingsModule.cs │ │ │ │ └── TraceModule.cs │ │ │ ├── NullLog.cs │ │ │ ├── RequestData.cs │ │ │ ├── RequestTraceSession.cs │ │ │ ├── Resources/ │ │ │ │ ├── 960.css │ │ │ │ ├── Modules/ │ │ │ │ │ ├── interactive/ │ │ │ │ │ │ ├── methods.js │ │ │ │ │ │ ├── providers.js │ │ │ │ │ │ └── results.js │ │ │ │ │ └── tracing/ │ │ │ │ │ ├── sessions.js │ │ │ │ │ └── traces.js │ │ │ │ ├── backbone-min.js │ │ │ │ ├── diagnostics.js │ │ │ │ ├── handlebars.js │ │ │ │ ├── interactive-diagnostics.js │ │ │ │ ├── interactive.css │ │ │ │ ├── jsonreport.js │ │ │ │ ├── main.css │ │ │ │ ├── nancy-common.js │ │ │ │ ├── request-tracing.js │ │ │ │ ├── reset.css │ │ │ │ ├── text.css │ │ │ │ └── underscore-min.js │ │ │ ├── ResponseData.cs │ │ │ ├── TemplateAttribute.cs │ │ │ ├── TestingDiagnosticProvider.cs │ │ │ └── Views/ │ │ │ ├── Dashboard.sshtml │ │ │ ├── Info.sshtml │ │ │ ├── InteractiveDiagnostics.sshtml │ │ │ ├── RequestTracing.sshtml │ │ │ ├── Settings.sshtml │ │ │ ├── _DiagnosticsMaster.sshtml │ │ │ ├── help.sshtml │ │ │ └── login.sshtml │ │ ├── DisabledStaticContentProvider.cs │ │ ├── DynamicDictionary.cs │ │ ├── DynamicDictionaryValue.cs │ │ ├── ErrorHandling/ │ │ │ ├── DefaultStatusCodeHandler.cs │ │ │ ├── Resources/ │ │ │ │ ├── 404.html │ │ │ │ └── 500.html │ │ │ └── RouteExecutionEarlyExitException.cs │ │ ├── ErrorPipeline.cs │ │ ├── Extensions/ │ │ │ ├── AssemblyExtensions.cs │ │ │ ├── CollectionExtensions.cs │ │ │ ├── ContextExtensions.cs │ │ │ ├── MemoryStreamExtensions.cs │ │ │ ├── ModelValidationErrorExtensions.cs │ │ │ ├── ModuleExtensions.cs │ │ │ ├── ObjectExtensions.cs │ │ │ ├── RequestExtensions.cs │ │ │ ├── StreamExtensions.cs │ │ │ ├── StringExtensions.cs │ │ │ └── TypeExtensions.cs │ │ ├── FormatterExtensions.cs │ │ ├── GlobalizationConfiguration.cs │ │ ├── GlobalizationConfigurationExtensions.cs │ │ ├── HeadResponse.cs │ │ ├── Helpers/ │ │ │ ├── CacheHelpers.cs │ │ │ ├── ExceptionExtensions.cs │ │ │ ├── HttpEncoder.cs │ │ │ ├── HttpUtility.cs │ │ │ ├── ProxyNancyReferenceProber.cs │ │ │ ├── ReflectionUtils.cs │ │ │ └── TaskHelpers.cs │ │ ├── HttpFile.cs │ │ ├── HttpLink.cs │ │ ├── HttpLinkBuilder.cs │ │ ├── HttpLinkRelation.cs │ │ ├── HttpMultipart.cs │ │ ├── HttpMultipartBoundary.cs │ │ ├── HttpMultipartBuffer.cs │ │ ├── HttpMultipartSubStream.cs │ │ ├── HttpStatusCode.cs │ │ ├── IAssemblyCatalog.cs │ │ ├── IHideObjectMembers.cs │ │ ├── INancyContextFactory.cs │ │ ├── INancyEngine.cs │ │ ├── INancyModule.cs │ │ ├── INancyModuleCatalog.cs │ │ ├── IO/ │ │ │ ├── RequestStream.cs │ │ │ └── UnclosableStreamWrapper.cs │ │ ├── IObjectSerializer.cs │ │ ├── IObjectSerializerSelector.cs │ │ ├── IResourceAssemblyProvider.cs │ │ ├── IResponseFormatter.cs │ │ ├── IResponseFormatterFactory.cs │ │ ├── IRootPathProvider.cs │ │ ├── IRuntimeEnvironmentInformation.cs │ │ ├── ISerializer.cs │ │ ├── ISerializerFactory.cs │ │ ├── IStaticContentProvider.cs │ │ ├── IStatusCodeHandler.cs │ │ ├── ITypeCatalog.cs │ │ ├── IncludeInNancyAssemblyScanningAttribute.cs │ │ ├── Json/ │ │ │ ├── Converters/ │ │ │ │ ├── TimeSpanConverter.cs │ │ │ │ └── TupleConverter.cs │ │ │ ├── DefaultJsonConfigurationProvider.cs │ │ │ ├── JavaScriptConverter.cs │ │ │ ├── JavaScriptPrimitiveConverter.cs │ │ │ ├── JavaScriptSerializer.cs │ │ │ ├── Json.cs │ │ │ ├── JsonConfiguration.cs │ │ │ ├── JsonConfigurationExtensions.cs │ │ │ ├── ScriptIgnoreAttribute.cs │ │ │ ├── Simple/ │ │ │ │ ├── NancySerializationStrategy.cs │ │ │ │ └── SimpleJson.cs │ │ │ └── SimpleJson.cs │ │ ├── Jsonp.cs │ │ ├── JsonpApplicationStartup.cs │ │ ├── Localization/ │ │ │ ├── ITextResource.cs │ │ │ ├── ResourceBasedTextResource.cs │ │ │ └── TextResourceFinder.cs │ │ ├── MimeTypes.cs │ │ ├── ModelBinding/ │ │ │ ├── BindingConfig.cs │ │ │ ├── BindingContext.cs │ │ │ ├── BindingDefaults.cs │ │ │ ├── BindingMemberInfo.cs │ │ │ ├── DefaultBinder.cs │ │ │ ├── DefaultBodyDeserializers/ │ │ │ │ ├── JsonBodyDeserializer.cs │ │ │ │ └── XmlBodyDeserializer.cs │ │ │ ├── DefaultConverters/ │ │ │ │ ├── CollectionConverter.cs │ │ │ │ ├── DateTimeConverter.cs │ │ │ │ ├── FallbackConverter.cs │ │ │ │ └── NumericConverter.cs │ │ │ ├── DefaultFieldNameConverter.cs │ │ │ ├── DefaultModelBinderLocator.cs │ │ │ ├── DynamicModelBinderAdapter.cs │ │ │ ├── ExpressionExtensions.cs │ │ │ ├── IBinder.cs │ │ │ ├── IBodyDeserializer.cs │ │ │ ├── IFieldNameConverter.cs │ │ │ ├── IModelBinder.cs │ │ │ ├── IModelBinderLocator.cs │ │ │ ├── ITypeConverter.cs │ │ │ ├── ModelBindingException.cs │ │ │ ├── ModuleExtensions.cs │ │ │ └── PropertyBindingException.cs │ │ ├── NamedPipelineBase.cs │ │ ├── Nancy.csproj │ │ ├── NancyContext.cs │ │ ├── NancyEngine.cs │ │ ├── NancyEngineExtensions.cs │ │ ├── NancyModule.cs │ │ ├── NegotiatorExtensions.cs │ │ ├── NotFoundResponse.cs │ │ ├── Owin/ │ │ │ ├── DelegateExtensions.cs │ │ │ ├── NancyContextExtensions.cs │ │ │ ├── NancyMiddleware.cs │ │ │ ├── NancyOptions.cs │ │ │ └── NancyOptionsExtensions.cs │ │ ├── PipelineItem.cs │ │ ├── Properties/ │ │ │ └── InternalsVisibleTo.cs │ │ ├── Request.cs │ │ ├── RequestExecutionException.cs │ │ ├── RequestHeaders.cs │ │ ├── ResourceAssemblyProvider.cs │ │ ├── Response.cs │ │ ├── ResponseExtensions.cs │ │ ├── Responses/ │ │ │ ├── DefaultJsonSerializer.cs │ │ │ ├── DefaultXmlSerializer.cs │ │ │ ├── EmbeddedFileResponse.cs │ │ │ ├── GenericFileResponse.cs │ │ │ ├── HtmlResponse.cs │ │ │ ├── JsonResponse.cs │ │ │ ├── MaterialisingResponse.cs │ │ │ ├── NegotiatedResponse.cs │ │ │ ├── Negotiation/ │ │ │ │ ├── DefaultResponseNegotiator.cs │ │ │ │ ├── IResponseNegotiator.cs │ │ │ │ ├── IResponseProcessor.cs │ │ │ │ ├── JsonProcessor.cs │ │ │ │ ├── MatchResult.cs │ │ │ │ ├── MediaRange.cs │ │ │ │ ├── MediaRangeParameters.cs │ │ │ │ ├── MediaType.cs │ │ │ │ ├── NegotiationContext.cs │ │ │ │ ├── Negotiator.cs │ │ │ │ ├── ProcessorMatch.cs │ │ │ │ ├── ResponseProcessor.cs │ │ │ │ ├── ViewProcessor.cs │ │ │ │ └── XmlProcessor.cs │ │ │ ├── NotAcceptableResponse.cs │ │ │ ├── RedirectResponse.cs │ │ │ ├── StreamResponse.cs │ │ │ ├── TextResponse.cs │ │ │ └── XmlResponse.cs │ │ ├── RouteConfiguration.cs │ │ ├── RouteConfigurationExtensions.cs │ │ ├── Routing/ │ │ │ ├── Constraints/ │ │ │ │ ├── AlphaRouteSegmentConstraint.cs │ │ │ │ ├── BoolRouteSegmentConstraint.cs │ │ │ │ ├── CustomDateTimeRouteSegmentConstraint.cs │ │ │ │ ├── DateTimeRouteSegmentConstraint.cs │ │ │ │ ├── DecimalRouteSegmentConstraint.cs │ │ │ │ ├── GuidRouteSegmentConstraint.cs │ │ │ │ ├── IRouteSegmentConstraint.cs │ │ │ │ ├── IntRouteSegmentConstraint.cs │ │ │ │ ├── LengthRouteSegmentConstraint.cs │ │ │ │ ├── LongRouteSegmentConstraint.cs │ │ │ │ ├── MaxLengthRouteSegmentConstraint.cs │ │ │ │ ├── MaxRouteSegmentConstraint.cs │ │ │ │ ├── MinLengthRouteSegmentConstraint.cs │ │ │ │ ├── MinRouteSegmentConstraint.cs │ │ │ │ ├── ParameterizedRouteSegmentConstraintBase.cs │ │ │ │ ├── RangeRouteSegmentConstraint.cs │ │ │ │ ├── RouteSegmentConstraintBase.cs │ │ │ │ └── VersionRouteSegmentConstraint.cs │ │ │ ├── DefaultNancyModuleBuilder.cs │ │ │ ├── DefaultRequestDispatcher.cs │ │ │ ├── DefaultRouteCacheProvider.cs │ │ │ ├── DefaultRouteDescriptionProvider.cs │ │ │ ├── DefaultRouteInvoker.cs │ │ │ ├── DefaultRouteResolver.cs │ │ │ ├── DefaultRouteSegmentExtractor.cs │ │ │ ├── INancyModuleBuilder.cs │ │ │ ├── IRequestDispatcher.cs │ │ │ ├── IRouteCache.cs │ │ │ ├── IRouteCacheProvider.cs │ │ │ ├── IRouteDescriptionProvider.cs │ │ │ ├── IRouteInvoker.cs │ │ │ ├── IRouteMetadataProvider.cs │ │ │ ├── IRouteResolver.cs │ │ │ ├── IRouteSegmentExtractor.cs │ │ │ ├── MethodNotAllowedRoute.cs │ │ │ ├── NotFoundRoute.cs │ │ │ ├── OptionsRoute.cs │ │ │ ├── ParameterSegmentInformation.cs │ │ │ ├── ResolveResult.cs │ │ │ ├── Route.cs │ │ │ ├── RouteCache.cs │ │ │ ├── RouteCacheExtensions.cs │ │ │ ├── RouteDescription.cs │ │ │ ├── RouteMetadata.cs │ │ │ ├── RouteMetadataProvider.cs │ │ │ └── Trie/ │ │ │ ├── IRouteResolverTrie.cs │ │ │ ├── ITrieNodeFactory.cs │ │ │ ├── MatchResult.cs │ │ │ ├── NodeData.cs │ │ │ ├── NodeDataExtensions.cs │ │ │ ├── Nodes/ │ │ │ │ ├── CaptureNode.cs │ │ │ │ ├── CaptureNodeWithConstraint.cs │ │ │ │ ├── CaptureNodeWithDefaultValue.cs │ │ │ │ ├── CaptureNodeWithMultipleParameters.cs │ │ │ │ ├── GreedyCaptureNode.cs │ │ │ │ ├── GreedyRegExCaptureNode.cs │ │ │ │ ├── LiteralNode.cs │ │ │ │ ├── OptionalCaptureNode.cs │ │ │ │ ├── RegExNode.cs │ │ │ │ ├── RootNode.cs │ │ │ │ └── TrieNode.cs │ │ │ ├── RouteResolverTrie.cs │ │ │ ├── SegmentMatch.cs │ │ │ └── TrieNodeFactory.cs │ │ ├── Security/ │ │ │ ├── ClaimsPrincipalExtensions.cs │ │ │ ├── Csrf.cs │ │ │ ├── CsrfApplicationStartup.cs │ │ │ ├── CsrfToken.cs │ │ │ ├── CsrfTokenExtensions.cs │ │ │ ├── CsrfTokenValidationResult.cs │ │ │ ├── CsrfValidationException.cs │ │ │ ├── DefaultCsrfTokenValidator.cs │ │ │ ├── ICsrfTokenValidator.cs │ │ │ ├── ModuleSecurity.cs │ │ │ ├── SSLProxy.cs │ │ │ └── SecurityHooks.cs │ │ ├── Session/ │ │ │ ├── CookieBasedSessions.cs │ │ │ ├── CookieBasedSessionsConfiguration.cs │ │ │ ├── ISession.cs │ │ │ ├── NullSessionProvider.cs │ │ │ └── Session.cs │ │ ├── StaticConfiguration.cs │ │ ├── StaticContent.cs │ │ ├── StaticContentConfiguration.cs │ │ ├── StaticContentConfigurationExtensions.cs │ │ ├── TinyIoc/ │ │ │ └── TinyIoC.cs │ │ ├── TraceConfiguration.cs │ │ ├── TraceConfigurationExtensions.cs │ │ ├── TypeCatalogExtensions.cs │ │ ├── TypeResolveStrategies.cs │ │ ├── TypeResolveStrategy.cs │ │ ├── Url.cs │ │ ├── Validation/ │ │ │ ├── CompositeValidator.cs │ │ │ ├── DefaultValidatorLocator.cs │ │ │ ├── IModelValidator.cs │ │ │ ├── IModelValidatorFactory.cs │ │ │ ├── IModelValidatorLocator.cs │ │ │ ├── ModelValidationDescriptor.cs │ │ │ ├── ModelValidationError.cs │ │ │ ├── ModelValidationException.cs │ │ │ ├── ModelValidationResult.cs │ │ │ ├── ModelValidationRule.cs │ │ │ ├── ModuleExtensions.cs │ │ │ └── Rules/ │ │ │ ├── ComparisonOperator.cs │ │ │ ├── ComparisonValidationRule.cs │ │ │ ├── NotEmptyValidationRule.cs │ │ │ ├── NotNullValidationRule.cs │ │ │ ├── RegexValidationRule.cs │ │ │ └── StringLengthValidationRule.cs │ │ ├── ViewConfiguration.cs │ │ ├── ViewConfigurationExtensions.cs │ │ ├── ViewEngines/ │ │ │ ├── AmbiguousViewsException.cs │ │ │ ├── DefaultFileSystemReader.cs │ │ │ ├── DefaultRenderContext.cs │ │ │ ├── DefaultRenderContextFactory.cs │ │ │ ├── DefaultResourceReader.cs │ │ │ ├── DefaultViewCache.cs │ │ │ ├── DefaultViewFactory.cs │ │ │ ├── DefaultViewLocator.cs │ │ │ ├── DefaultViewResolver.cs │ │ │ ├── Extensions.cs │ │ │ ├── FileSystemViewLocationProvider.cs │ │ │ ├── FileSystemViewLocationResult.cs │ │ │ ├── IFileSystemReader.cs │ │ │ ├── IRenderContext.cs │ │ │ ├── IRenderContextFactory.cs │ │ │ ├── IResourceReader.cs │ │ │ ├── IViewCache.cs │ │ │ ├── IViewEngine.cs │ │ │ ├── IViewFactory.cs │ │ │ ├── IViewLocationProvider.cs │ │ │ ├── IViewLocator.cs │ │ │ ├── IViewResolver.cs │ │ │ ├── ResourceViewLocationProvider.cs │ │ │ ├── SuperSimpleViewEngine/ │ │ │ │ ├── ISuperSimpleViewEngineMatcher.cs │ │ │ │ ├── IViewEngineHost.cs │ │ │ │ ├── NancyViewEngineHost.cs │ │ │ │ ├── SuperSimpleViewEngine.cs │ │ │ │ ├── SuperSimpleViewEngineRegistrations.cs │ │ │ │ └── SuperSimpleViewEngineWrapper.cs │ │ │ ├── ViewEngineApplicationStartup.cs │ │ │ ├── ViewEngineStartupContext.cs │ │ │ ├── ViewLocationContext.cs │ │ │ ├── ViewLocationResult.cs │ │ │ ├── ViewNotFoundException.cs │ │ │ └── ViewRenderException.cs │ │ ├── ViewRenderer.cs │ │ └── Xml/ │ │ ├── DefaultXmlConfigurationProvider.cs │ │ ├── XmlConfiguration.cs │ │ └── XmlConfigurationExtensions.cs │ ├── Nancy.Authentication.Basic/ │ │ ├── BasicAuthentication.cs │ │ ├── BasicAuthenticationConfiguration.cs │ │ ├── BasicHttpExtensions.cs │ │ ├── IUserValidator.cs │ │ ├── Nancy.Authentication.Basic.csproj │ │ └── UserPromptBehaviour.cs │ ├── Nancy.Authentication.Forms/ │ │ ├── FormsAuthentication.cs │ │ ├── FormsAuthenticationConfiguration.cs │ │ ├── IUserMapper.cs │ │ ├── ModuleExtensions.cs │ │ └── Nancy.Authentication.Forms.csproj │ ├── Nancy.Authentication.Stateless/ │ │ ├── Nancy.Authentication.Stateless.csproj │ │ ├── StatelessAuthentication.cs │ │ └── StatelessAuthenticationConfiguration.cs │ ├── Nancy.Embedded/ │ │ ├── Conventions/ │ │ │ └── EmbeddedStaticContentConventionBuilder.cs │ │ └── Nancy.Embedded.csproj │ ├── Nancy.Encryption.MachineKey/ │ │ ├── MachineKeyCryptographyConfigurations.cs │ │ ├── MachineKeyEncryptionProvider.cs │ │ ├── MachineKeyHmacProvider.cs │ │ └── Nancy.Encryption.MachineKey.csproj │ ├── Nancy.Hosting.Aspnet/ │ │ ├── AspNetRootPathProvider.cs │ │ ├── BootstrapperEntry.cs │ │ ├── DefaultNancyAspNetBootstrapper.cs │ │ ├── Nancy.Hosting.Aspnet.csproj │ │ ├── NancyFxSection.cs │ │ ├── NancyHandler.cs │ │ ├── NancyHttpRequestHandler.cs │ │ ├── NancyResponseStream.cs │ │ ├── TinyIoCAspNetExtensions.cs │ │ └── web.config.transform │ ├── Nancy.Hosting.Self/ │ │ ├── AutomaticUrlReservationCreationFailureException.cs │ │ ├── FileSystemRootPathProvider.cs │ │ ├── HostConfiguration.cs │ │ ├── IgnoredHeaders.cs │ │ ├── Nancy.Hosting.Self.csproj │ │ ├── NancyHost.cs │ │ ├── NetSh.cs │ │ ├── Properties/ │ │ │ └── InternalsVisibleTo.cs │ │ ├── UacHelper.cs │ │ ├── UriExtensions.cs │ │ └── UrlReservations.cs │ ├── Nancy.Metadata.Modules/ │ │ ├── DefaultMetadataModuleConventions.cs │ │ ├── DefaultMetadataModuleResolver.cs │ │ ├── IMetadataModule.cs │ │ ├── IMetadataModuleResolver.cs │ │ ├── MetadataModule.cs │ │ ├── MetadataModuleRegistrations.cs │ │ ├── MetadataModuleRouteMetadataProvider.cs │ │ └── Nancy.Metadata.Modules.csproj │ ├── Nancy.Owin/ │ │ ├── AppBuilderExtensions.cs │ │ └── Nancy.Owin.csproj │ ├── Nancy.Testing/ │ │ ├── Accept.cs │ │ ├── AndConnector.cs │ │ ├── AssertEqualityComparer.cs │ │ ├── AssertException.cs │ │ ├── AssertExtensions.cs │ │ ├── Asserts.cs │ │ ├── Browser.cs │ │ ├── BrowserContext.cs │ │ ├── BrowserContextExtensions.cs │ │ ├── BrowserContextMultipartFormData.cs │ │ ├── BrowserResponse.cs │ │ ├── BrowserResponseBodyWrapper.cs │ │ ├── BrowserResponseBodyWrapperExtensions.cs │ │ ├── BrowserResponseExtensions.cs │ │ ├── ConfigurableBootstrapper.cs │ │ ├── ConfigurableNancyModule.cs │ │ ├── DocumentWrapper.cs │ │ ├── IBrowserContextValues.cs │ │ ├── IndexHelper.cs │ │ ├── Nancy.Testing.csproj │ │ ├── NancyContextExtensions.cs │ │ ├── NodeWrapper.cs │ │ ├── PassThroughStatusHandler.cs │ │ ├── PathHelper.cs │ │ ├── QueryWrapper.cs │ │ ├── Resources/ │ │ │ └── NancyTestingCert.pfx │ │ ├── StaticConfigurationContext.cs │ │ ├── TestingViewBrowserResponseExtensions.cs │ │ ├── TestingViewContextKeys.cs │ │ └── TestingViewFactory.cs │ ├── Nancy.Validation.DataAnnotations/ │ │ ├── DataAnnotationsRegistrations.cs │ │ ├── DataAnnotationsValidator.cs │ │ ├── DataAnnotationsValidatorAdapter.cs │ │ ├── DataAnnotationsValidatorFactory.cs │ │ ├── DefaultPropertyValidatorFactory.cs │ │ ├── DefaultValidatableObjectAdapter.cs │ │ ├── IDataAnnotationsValidatorAdapter.cs │ │ ├── IPropertyValidator.cs │ │ ├── IPropertyValidatorFactory.cs │ │ ├── IValidatableObjectAdapter.cs │ │ ├── Nancy.Validation.DataAnnotations.csproj │ │ ├── PropertyValidator.cs │ │ ├── RangeValidatorAdapter.cs │ │ ├── RegexValidatorAdapter.cs │ │ ├── RequiredValidatorAdapter.cs │ │ └── StringLengthValidatorAdapter.cs │ ├── Nancy.Validation.FluentValidation/ │ │ ├── AdapterBase.cs │ │ ├── DefaultFluentAdapterFactory.cs │ │ ├── EmailAdapter.cs │ │ ├── EqualAdapter.cs │ │ ├── ExactLengthAdapater.cs │ │ ├── ExclusiveBetweenAdapter.cs │ │ ├── FallbackAdapter.cs │ │ ├── FluentValidationRegistrations.cs │ │ ├── FluentValidationValidator.cs │ │ ├── FluentValidationValidatorFactory.cs │ │ ├── GreaterThanAdapter.cs │ │ ├── GreaterThanOrEqualAdapter.cs │ │ ├── IFluentAdapter.cs │ │ ├── IFluentAdapterFactory.cs │ │ ├── InclusiveBetweenAdapter.cs │ │ ├── LengthAdapter.cs │ │ ├── LessThanAdapter.cs │ │ ├── LessThanOrEqualAdapter.cs │ │ ├── Nancy.Validation.FluentValidation.csproj │ │ ├── NotEmptyAdapter.cs │ │ ├── NotEqualAdapter.cs │ │ ├── NotNullAdapter.cs │ │ └── RegularExpressionAdapter.cs │ ├── Nancy.ViewEngines.DotLiquid/ │ │ ├── DefaultFileSystemFactory.cs │ │ ├── DotLiquidRegistrations.cs │ │ ├── DotLiquidViewEngine.cs │ │ ├── DynamicDrop.cs │ │ ├── IFileSystemFactory.cs │ │ ├── LiquidNancyFileSystem.cs │ │ ├── Nancy.ViewEngines.DotLiquid.csproj │ │ └── Resources/ │ │ └── 500.liquid │ ├── Nancy.ViewEngines.Markdown/ │ │ ├── MarkDownViewEngine.cs │ │ ├── MarkdownViewEngineHost.cs │ │ ├── MarkdownViewengineRender.cs │ │ └── Nancy.ViewEngines.Markdown.csproj │ ├── Nancy.ViewEngines.Nustache/ │ │ ├── Nancy.ViewEngines.Nustache.csproj │ │ └── NustacheViewEngine.cs │ ├── Nancy.ViewEngines.Razor/ │ │ ├── AttributeValue.cs │ │ ├── CSharp/ │ │ │ ├── CSharpClrTypeResolver.cs │ │ │ ├── CSharpRazorViewRenderer.cs │ │ │ └── NancyCSharpRazorCodeParser.cs │ │ ├── ClrTypeResolver.cs │ │ ├── CodeParserHelper.cs │ │ ├── DefaultRazorConfiguration.cs │ │ ├── EncodedHtmlString.cs │ │ ├── HelperResult.cs │ │ ├── HtmlHelpers.cs │ │ ├── HtmlHelpersExtensions.cs │ │ ├── IHtmlString.cs │ │ ├── INancyRazorView.cs │ │ ├── IRazorConfiguration.cs │ │ ├── IRazorViewRenderer.cs │ │ ├── ModelCodeGenerator.cs │ │ ├── Nancy.ViewEngines.Razor.csproj │ │ ├── NancyRazorEngineHost.cs │ │ ├── NancyRazorErrorView.cs │ │ ├── NancyRazorViewBase.cs │ │ ├── NonEncodedHtmlString.cs │ │ ├── RazorAssemblyProvider.cs │ │ ├── RazorConfigurationSection.cs │ │ ├── RazorViewEngine.cs │ │ ├── RazorViewEngineApplicationStartupRegistrations.cs │ │ ├── Resources/ │ │ │ └── CompilationError.html │ │ ├── UrlHelpers.cs │ │ ├── app.config.transform │ │ ├── targets/ │ │ │ └── Nancy.ViewEngines.Razor.targets │ │ └── web.config.transform │ ├── Nancy.ViewEngines.Razor.BuildProviders/ │ │ ├── Nancy.ViewEngines.Razor.BuildProviders.csproj │ │ └── NancyCSharpRazorBuildProvider.cs │ └── Nancy.ViewEngines.Spark/ │ ├── Descriptors/ │ │ ├── BuildDescriptorParams.cs │ │ ├── DefaultDescriptorBuilder.cs │ │ ├── DescriptorFilterExtensions.cs │ │ ├── IDescriptorBuilder.cs │ │ └── IDescriptorFilter.cs │ ├── Nancy.ViewEngines.Spark.csproj │ ├── NancyBindingProvider.cs │ ├── NancySparkView.cs │ ├── NancyViewData.cs │ ├── NancyViewFolder.cs │ ├── SparkRenderContextWrapper.cs │ ├── SparkViewEngine.cs │ └── SparkViewEngineResult.cs ├── test/ │ ├── Directory.Build.props │ ├── Nancy.Authentication.Basic.Tests/ │ │ ├── BasicAuthenticationConfigurationFixture.cs │ │ ├── BasicAuthenticationFixture.cs │ │ └── Nancy.Authentication.Basic.Tests.csproj │ ├── Nancy.Authentication.Forms.Tests/ │ │ ├── FormsAuthenticationConfigurationFixture.cs │ │ ├── FormsAuthenticationFixture.cs │ │ └── Nancy.Authentication.Forms.Tests.csproj │ ├── Nancy.Embedded.Tests/ │ │ ├── Nancy.Embedded.Tests.csproj │ │ ├── Resources/ │ │ │ ├── Subfolder/ │ │ │ │ └── embedded2.txt │ │ │ ├── Subfolder-with-hyphen/ │ │ │ │ └── embedded3.txt │ │ │ └── embedded.txt │ │ └── Unit/ │ │ └── EmbeddedStaticContentConventionBuilderFixture.cs │ ├── Nancy.Encryption.MachineKey.Tests/ │ │ ├── MachineConfigEncryptionProviderFixture.cs │ │ ├── MachineKeyHmacProviderFixture.cs │ │ └── Nancy.Encryption.MachineKey.Tests.csproj │ ├── Nancy.Hosting.Aspnet.Tests/ │ │ ├── Nancy.Hosting.Aspnet.Tests.csproj │ │ └── NancyHandlerFixture.cs │ ├── Nancy.Hosting.Self.Tests/ │ │ ├── IsCaseInstensitiveBaseOfFixture.cs │ │ ├── MakeAppLocalPathFixture.cs │ │ ├── Nancy.Hosting.Self.Tests.csproj │ │ ├── NancySelfHostFixture.cs │ │ └── TestModule.cs │ ├── Nancy.Metadata.Modules.Tests/ │ │ ├── DefaultMetadataModuleConventionsFixture.cs │ │ ├── FakeNancyMetadataModule.cs │ │ ├── FakeNancyModule.cs │ │ ├── Metadata/ │ │ │ └── FakeNancyMetadataModule.cs │ │ ├── MetadataModuleFixture.cs │ │ ├── MetadataModuleRouteMetadataProviderFixture.cs │ │ ├── Modules/ │ │ │ └── FakeNancyModule.cs │ │ └── Nancy.Metadata.Modules.Tests.csproj │ ├── Nancy.Owin.Tests/ │ │ ├── AppBuilderExtensionsFixture.cs │ │ └── Nancy.Owin.Tests.csproj │ ├── Nancy.Testing.Tests/ │ │ ├── AndConnectorTests.cs │ │ ├── AssertEqualityComparerFixture.cs │ │ ├── AssertExtensionsTests.cs │ │ ├── BrowserContextExtensionsFixture.cs │ │ ├── BrowserDefaultsFixture.cs │ │ ├── BrowserFixture.cs │ │ ├── BrowserResponseBodyWrapperExtensionsFixture.cs │ │ ├── BrowserResponseBodyWrapperFixture.cs │ │ ├── BrowserResponseExtensionsTests.cs │ │ ├── CaseSensitivityFixture.cs │ │ ├── ConfigurableBootstrapperDependenciesTests.cs │ │ ├── ConfigurableBootstrapperFixture.cs │ │ ├── ContextExtensionsTests.cs │ │ ├── DocumentWrapperTests.cs │ │ ├── Nancy.Testing.Tests.csproj │ │ ├── PathHelperTests.cs │ │ ├── QueryWrapperTests.cs │ │ └── TestingViewExtensions/ │ │ ├── GetModelExtententionsTests.cs │ │ ├── GetModuleNameExtensionTests.cs │ │ ├── GetModulePathExtensionMethodTests.cs │ │ ├── GetViewNameExtensionTests.cs │ │ ├── TestingViewFactoryTestModule.cs │ │ └── ViewFactoryTest.sshtml │ ├── Nancy.Tests/ │ │ ├── Extensions/ │ │ │ └── ResponseExtensions.cs │ │ ├── Fakes/ │ │ │ ├── FakeDefaultNancyBootstrapper.cs │ │ │ ├── FakeHookedModule.cs │ │ │ ├── FakeModuleCatalog.cs │ │ │ ├── FakeNancyModule.cs │ │ │ ├── FakeNancyModuleNoRoutes.cs │ │ │ ├── FakeNancyModuleWithBasePath.cs │ │ │ ├── FakeNancyModuleWithDependency.cs │ │ │ ├── FakeNancyModuleWithPreAndPostHooks.cs │ │ │ ├── FakeNancyModuleWithoutBasePath.cs │ │ │ ├── FakeObjectSerializer.cs │ │ │ ├── FakeRequest.cs │ │ │ ├── FakeRoute.cs │ │ │ ├── FakeRouteCache.cs │ │ │ ├── FakeRouteResolver.cs │ │ │ ├── FakeViewEngine.cs │ │ │ ├── FakeViewEngineHost.cs │ │ │ ├── MockPipelines.cs │ │ │ ├── Person.cs │ │ │ ├── PersonWithAgeField.cs │ │ │ ├── StructModel.cs │ │ │ └── ViewModel.cs │ │ ├── Helpers/ │ │ │ ├── AssemblyHelpers.cs │ │ │ ├── CacheHelpersFixture.cs │ │ │ └── ExceptionExtensionsFixture.cs │ │ ├── Nancy.Tests.csproj │ │ ├── NoAppStartupsFixture.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ ├── Assets/ │ │ │ │ └── Styles/ │ │ │ │ ├── Sub/ │ │ │ │ │ └── styles.css │ │ │ │ ├── Sub.folder/ │ │ │ │ │ └── styles.css │ │ │ │ ├── css/ │ │ │ │ │ └── styles.css │ │ │ │ ├── dotted.filename.css │ │ │ │ ├── space in name.css │ │ │ │ ├── strange-css-filename.css │ │ │ │ └── styles.css │ │ │ ├── Link.Texts.Designer.cs │ │ │ ├── Link.Texts.resx │ │ │ ├── Menu.Designer.cs │ │ │ ├── Menu.Texts.Designer.cs │ │ │ ├── Menu.Texts.resx │ │ │ ├── Menu.resx │ │ │ ├── Views/ │ │ │ │ ├── SuperSimpleViewEngineSampleContent.cs │ │ │ │ └── staticviewresource.html │ │ │ └── test.txt │ │ ├── Responses/ │ │ │ └── MaterialisingResponseFixture.cs │ │ ├── ShouldExtensions.cs │ │ ├── Unit/ │ │ │ ├── AfterPipelineFixture.cs │ │ │ ├── AppDomainAssemblyCatalogFixture.cs │ │ │ ├── BeforePipelineFixture.cs │ │ │ ├── Bootstrapper/ │ │ │ │ ├── Base/ │ │ │ │ │ ├── BootstrapperBaseFixtureBase.cs │ │ │ │ │ └── ModuleCatalogFixtureBase.cs │ │ │ │ ├── BootstrapperLocatorFixture.cs │ │ │ │ ├── CollectionTypeRegistrationFixture.cs │ │ │ │ ├── InstanceRegistrationFixture.cs │ │ │ │ ├── NancyBootstrapperBaseFixture.cs │ │ │ │ ├── NancyBootstrapperWithRequestContainerBaseFixture.cs │ │ │ │ ├── NancyInternalConfigurationFixture.cs │ │ │ │ ├── PipelinesFixture.cs │ │ │ │ └── TypeRegistrationFixture.cs │ │ │ ├── Configuration/ │ │ │ │ ├── DefaultNancyEnvironmentConfiguratorFixture.cs │ │ │ │ ├── DefaultNancyEnvironmentFactoryFixture.cs │ │ │ │ ├── DefaultNancyEnvironmentFixture.cs │ │ │ │ └── INancyEnvironmentExtensionsFixture.cs │ │ │ ├── Conventions/ │ │ │ │ ├── DefaultAcceptHeaderCoercionConventionsFixture.cs │ │ │ │ ├── DefaultCultureConventionsFixture.cs │ │ │ │ ├── DefaultStaticContentsConventionsFixture.cs │ │ │ │ └── DefaultViewLocationConventionsFixture.cs │ │ │ ├── Cryptography/ │ │ │ │ ├── AesEncryptionProviderFixture.cs │ │ │ │ ├── DefaultHmacProviderFixture.cs │ │ │ │ ├── HmacComparerFixture.cs │ │ │ │ └── NoEncryptionProviderFixture.cs │ │ │ ├── Culture/ │ │ │ │ └── BuiltInCultureConventionFixture.cs │ │ │ ├── DefaultNancyBootstrapperBootstrapperBaseFixture.cs │ │ │ ├── DefaultNancyBootstrapperFixture.cs │ │ │ ├── DefaultNancyBootstrapperModuleCatalogFixture.cs │ │ │ ├── DefaultResponseFormatterFactoryFixture.cs │ │ │ ├── DefaultResponseFormatterFixture.cs │ │ │ ├── DefaultSerializerFactoryFixture.cs │ │ │ ├── Diagnostics/ │ │ │ │ ├── ConcurrentLimitedCollectionFixture.cs │ │ │ │ ├── CustomInteractiveDiagnosticsFixture.cs │ │ │ │ ├── DefaultRequestTraceFactoryFixture.cs │ │ │ │ ├── DiagnosticsHookFixture.cs │ │ │ │ └── InteractiveDiagnosticsFixture.cs │ │ │ ├── DynamicDictionaryFixture.cs │ │ │ ├── DynamicDictionaryValueFixture.cs │ │ │ ├── ErrorHandling/ │ │ │ │ └── DefaultStatusCodeHandlerFixture.cs │ │ │ ├── ErrorPipelineFixture.cs │ │ │ ├── Extensions/ │ │ │ │ ├── ContextExtensionsFixture.cs │ │ │ │ ├── RequestExtensionsFixture.cs │ │ │ │ ├── StreamExtensionsFixture.cs │ │ │ │ ├── StringExtensionsFixture.cs │ │ │ │ └── TypeExtensionsFixture.cs │ │ │ ├── FormatterExtensionsFixture.cs │ │ │ ├── HeadResponseFixture.cs │ │ │ ├── Helpers/ │ │ │ │ └── HttpUtilityFixture.cs │ │ │ ├── HttpLinkBuilderFixture.cs │ │ │ ├── HttpLinkFixture.cs │ │ │ ├── HttpLinkRelationFixture.cs │ │ │ ├── HttpMultipartBoundaryFixture.cs │ │ │ ├── HttpMultipartBufferFixture.cs │ │ │ ├── HttpMultipartFixture.cs │ │ │ ├── IO/ │ │ │ │ └── RequestStreamFixture.cs │ │ │ ├── Json/ │ │ │ │ ├── JavaScriptSerializerFixture.cs │ │ │ │ ├── Simple/ │ │ │ │ │ └── NancySerializationStrategyFixture.cs │ │ │ │ ├── SimpleJsonFixture.cs │ │ │ │ ├── TestConverter.cs │ │ │ │ ├── TestConverterType.cs │ │ │ │ ├── TestData.cs │ │ │ │ ├── TestPrimitiveConverter.cs │ │ │ │ ├── TestPrimitiveConverterType.cs │ │ │ │ └── TypeWithTuple.cs │ │ │ ├── JsonFormatterExtensionsFixtures.cs │ │ │ ├── JsonSerializerFixture.cs │ │ │ ├── Localization/ │ │ │ │ └── ResourceBasedTextResourceFixture.cs │ │ │ ├── MimeTypesFixture.cs │ │ │ ├── ModelBinding/ │ │ │ │ ├── BindingMemberInfoFixture.cs │ │ │ │ ├── DefaultBinderFixture.cs │ │ │ │ ├── DefaultBodyDeserializers/ │ │ │ │ │ ├── JsonBodyDeserializerFixture.cs │ │ │ │ │ └── XmlBodyDeserializerfixture.cs │ │ │ │ ├── DefaultConverters/ │ │ │ │ │ ├── CollectionConverterFixture.cs │ │ │ │ │ └── FallbackConverterFixture.cs │ │ │ │ ├── DefaultFieldNameConverterFixture.cs │ │ │ │ ├── DefaultModelBinderLocatorFixture.cs │ │ │ │ ├── DynamicModelBinderAdapterFixture.cs │ │ │ │ ├── ModelBindingExceptionFixture.cs │ │ │ │ └── PropertyBindingExceptionFixture.cs │ │ │ ├── ModuleNameFixture.cs │ │ │ ├── NamedPipelineBaseFixture.cs │ │ │ ├── NancyContextFixture.cs │ │ │ ├── NancyCookieFixture.cs │ │ │ ├── NancyEngineFixture.cs │ │ │ ├── NancyMiddlewareFixture.cs │ │ │ ├── NancyModuleFixture.cs │ │ │ ├── NancyOptionsExtensionsFixture.cs │ │ │ ├── NancyOptionsFixture.cs │ │ │ ├── RequestFixture.cs │ │ │ ├── RequestHeadersFixture.cs │ │ │ ├── ResponseExtensionsFixture.cs │ │ │ ├── ResponseFixture.cs │ │ │ ├── Responses/ │ │ │ │ ├── DefaultJsonSerializerFixture.cs │ │ │ │ ├── EmbeddedFileResponseFixture.cs │ │ │ │ ├── GenericFileResponseFixture.cs │ │ │ │ ├── Negotiation/ │ │ │ │ │ ├── DefaultResponseNegotiatorFixture.cs │ │ │ │ │ └── MediaRangeFixture.cs │ │ │ │ ├── RedirectResponseFixture.cs │ │ │ │ ├── StreamResponseFixture.cs │ │ │ │ └── TextResponseFixture.cs │ │ │ ├── Routing/ │ │ │ │ ├── ConstraintNodeRouteResolverFixture.cs │ │ │ │ ├── ConstraintNodeRouteScoringFixture.cs │ │ │ │ ├── DefaultNancyModuleBuilderFixture.cs │ │ │ │ ├── DefaultRequestDispatcherFixture.cs │ │ │ │ ├── DefaultRouteCacheProviderFixture.cs │ │ │ │ ├── DefaultRouteInvokerFixture.cs │ │ │ │ ├── DefaultRouteResolverFixture.cs │ │ │ │ ├── DefaultRouteSegmentExtractorFixture.cs │ │ │ │ ├── NotFoundRouteFixture.cs │ │ │ │ ├── RouteCacheFixture.cs │ │ │ │ ├── RouteDescriptionFixture.cs │ │ │ │ └── RouteFixture.cs │ │ │ ├── Security/ │ │ │ │ ├── ClaimsPrincipalExtensionsFixture.cs │ │ │ │ ├── CsrfFixture.cs │ │ │ │ ├── DefaultCsrfTokenValidatorFixture.cs │ │ │ │ ├── ModuleSecurityFixture.cs │ │ │ │ └── SSLProxyFixture.cs │ │ │ ├── Sessions/ │ │ │ │ ├── CookieBasedSessionsConfigurationFixture.cs │ │ │ │ ├── CookieBasedSessionsFixture.cs │ │ │ │ ├── DefaultSessionObjectFormatterFixture.cs │ │ │ │ ├── NullSessionProviderFixture.cs │ │ │ │ └── SessionFixture.cs │ │ │ ├── StaticContentConventionBuilderFixture.cs │ │ │ ├── TextFormatterFixture.cs │ │ │ ├── UrlFixture.cs │ │ │ ├── Validation/ │ │ │ │ ├── CompositeValidatorFixture.cs │ │ │ │ ├── DefaultValidatorLocatorFixture.cs │ │ │ │ ├── ModuleExtensionsFixture.cs │ │ │ │ └── ValidationResultFixture.cs │ │ │ ├── ViewEngines/ │ │ │ │ ├── DefaultRenderContextFixture.cs │ │ │ │ ├── DefaultViewFactoryFixture.cs │ │ │ │ ├── DefaultViewLocatorFixture.cs │ │ │ │ ├── DefaultViewResolverFixture.cs │ │ │ │ ├── FileSystemViewLocationProviderFixture.cs │ │ │ │ ├── FileSystemViewLocationResultFixture.cs │ │ │ │ ├── ResourceViewLocationProviderFixture.cs │ │ │ │ ├── SuperSimpleViewEngineTests.cs │ │ │ │ ├── ViewEngineStartupFixture.cs │ │ │ │ └── ViewNotFoundExceptionFixture.cs │ │ │ └── XmlFormatterExtensionsFixtures.cs │ │ └── xUnitExtensions/ │ │ ├── RecordAsync.cs │ │ ├── Skip.cs │ │ ├── SkipException.cs │ │ ├── SkippableFactAttribute.cs │ │ └── UsingCultureAttribute.cs │ ├── Nancy.Tests.Functional/ │ │ ├── Hidden.txt │ │ ├── Modules/ │ │ │ ├── AbsoluteUrlTestModule.cs │ │ │ ├── CookieModule.cs │ │ │ ├── JsonpTestModule.cs │ │ │ ├── PerRouteAuthModule.cs │ │ │ ├── RazorTestModule.cs │ │ │ ├── RazorWithTracingTestModule.cs │ │ │ ├── SerializeTestModule.cs │ │ │ ├── SerializerTestModule.cs │ │ │ └── ThrowingModule.cs │ │ ├── Nancy.Tests.Functional.csproj │ │ ├── Tests/ │ │ │ ├── AbsoluteUrlTests.cs │ │ │ ├── AsyncExceptionTests.cs │ │ │ ├── BasicRouteInvocationsFixture.cs │ │ │ ├── ContentNegotiationFixture.cs │ │ │ ├── CookieFixture.cs │ │ │ ├── JsonLdProcessor.cs │ │ │ ├── JsonpTests.cs │ │ │ ├── ManualStaticContentTests.cs │ │ │ ├── MethodRewriteFixture.cs │ │ │ ├── ModelBindingTests.cs │ │ │ ├── PartialViewTests.cs │ │ │ ├── PerRouteAuthFixture.cs │ │ │ ├── RouteConstraintTests.cs │ │ │ ├── SerializeTests.cs │ │ │ ├── SerializerTests.cs │ │ │ ├── TracingSmokeTests.cs │ │ │ └── ViewBagTests.cs │ │ └── Views/ │ │ ├── RazorPage.cshtml │ │ ├── RazorPageWithUnknownPartial.cshtml │ │ ├── _LayoutPage.cshtml │ │ ├── _PartialTest.cshtml │ │ └── _ViewStart.cshtml │ ├── Nancy.Validation.DataAnnotations.Tests/ │ │ ├── DataAnnotationsValidatorFactoryFixture.cs │ │ ├── DataAnnotationsValidatorFixture.cs │ │ ├── DefaultValidatableObjectAdapterFixture.cs │ │ ├── Nancy.Validation.DataAnnotations.Tests.csproj │ │ └── PropertyValidatorFixture.cs │ ├── Nancy.Validation.FluentValidation.Tests/ │ │ ├── DefaultFluentAdapterFactoryFixture.cs │ │ ├── EmailAdapterFixture.cs │ │ ├── FluentValidationValidatorFactoryFixture.cs │ │ └── Nancy.Validation.FluentValidation.Tests.csproj │ ├── Nancy.ViewEngines.DotLiquid.Tests/ │ │ ├── DotLiquidViewEngineFixture.cs │ │ ├── DynamicDropFixture.cs │ │ ├── FakeModel.cs │ │ ├── Functional/ │ │ │ └── PartialRenderingFixture.cs │ │ ├── LiquidNancyFileSystemFixture.cs │ │ ├── Nancy.ViewEngines.DotLiquid.Tests.csproj │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── Views/ │ │ ├── doublequotedpartial.liquid │ │ ├── partial.liquid │ │ ├── singlequotedpartial.liquid │ │ └── unquotedpartial.liquid │ ├── Nancy.ViewEngines.Markdown.Tests/ │ │ ├── Markdown/ │ │ │ ├── home.md │ │ │ ├── htmlmaster.html │ │ │ ├── master.md │ │ │ ├── partial.markdown │ │ │ ├── standalone.md │ │ │ └── viewwithhtmlmaster.md │ │ ├── MarkdownViewEngineFixture.cs │ │ ├── MarkdownViewengineRenderFixture.cs │ │ ├── Nancy.ViewEngines.Markdown.Tests.csproj │ │ └── UserModel.cs │ ├── Nancy.ViewEngines.Razor.Tests/ │ │ ├── GreetingViewBase.cs │ │ ├── Nancy.ViewEngines.Razor.Tests.csproj │ │ ├── RazorViewEngineFixture.cs │ │ ├── TestModel.cs │ │ ├── TestViews/ │ │ │ ├── Layouts/ │ │ │ │ ├── LayoutWithManySections.cshtml │ │ │ │ ├── LayoutWithOptionalSection.cshtml │ │ │ │ ├── LayoutWithOptionalSectionWithDefaults.cshtml │ │ │ │ ├── LayoutWithSection.cshtml │ │ │ │ └── SimplyLayout.cshtml │ │ │ ├── ViewThatUsesAttributeWithCodeInside.cshtml │ │ │ ├── ViewThatUsesAttributeWithDynamicNullInside.cshtml │ │ │ ├── ViewThatUsesAttributeWithHtmlStringInside.cshtml │ │ │ ├── ViewThatUsesAttributeWithNonEncodedHtmlStringInside.cshtml │ │ │ ├── ViewThatUsesAttributeWithRawHtmlStringInside.cshtml │ │ │ ├── ViewThatUsesHelper.cshtml │ │ │ ├── ViewThatUsesLayout.cshtml │ │ │ ├── ViewThatUsesLayoutAndManySection.cshtml │ │ │ ├── ViewThatUsesLayoutAndModel.cshtml │ │ │ ├── ViewThatUsesLayoutAndOptionalSection.cshtml │ │ │ ├── ViewThatUsesLayoutAndOptionalSectionOverridingDefaults.cshtml │ │ │ ├── ViewThatUsesLayoutAndOptionalSectionWithDefaults.cshtml │ │ │ ├── ViewThatUsesLayoutAndSection.cshtml │ │ │ ├── ViewThatUsesModelCSharp.cshtml │ │ │ └── ViewThatUsesModelVB.vbhtml │ │ └── TextResourceFinderFixture.cs │ ├── Nancy.ViewEngines.Razor.Tests.Models/ │ │ ├── Hobby.cs │ │ ├── Nancy.ViewEngines.Razor.Tests.Models.csproj │ │ └── Person.cs │ └── Nancy.ViewEngines.Spark.Tests/ │ ├── App.config │ ├── Nancy.ViewEngines.Spark.Tests.csproj │ ├── NancyViewFolderFixture.cs │ ├── ShadeViews/ │ │ ├── Features/ │ │ │ ├── ShadeCodeMayBeDashOrAtBraced.shade │ │ │ ├── ShadeElementsMayStackOnOneLine.shade │ │ │ ├── ShadeEvaluatesExpressions.shade │ │ │ ├── ShadeFileRenders.shade │ │ │ ├── ShadeSupportsAttributesAndMayTreatSomeElementsAsSpecialNodes.shade │ │ │ ├── ShadeTextMayContainExpressions.shade │ │ │ └── ShadeThatUsesApplicationLayout.shade │ │ └── Shared/ │ │ ├── Application.shade │ │ └── _SimpleValue.spark │ ├── SparkViewEngineFixture.cs │ ├── TestViews/ │ │ ├── Layouts/ │ │ │ └── anotherLayout.spark │ │ ├── Shared/ │ │ │ ├── Application.spark │ │ │ ├── Partial.spark │ │ │ ├── elementLayout.spark │ │ │ └── layout.spark │ │ └── Stub/ │ │ ├── Index.spark │ │ ├── List.spark │ │ ├── PartialTarget.spark │ │ ├── Subfolder/ │ │ │ └── Subfolderview.spark │ │ ├── ViewThatChangesGlobalSettings.spark │ │ ├── ViewThatExpectsALayout.spark │ │ ├── ViewThatRendersPartialsThatShareState.spark │ │ ├── ViewThatUsesANullViewModel.spark │ │ ├── ViewThatUsesAllNamedContentAreas.spark │ │ ├── ViewThatUsesAnonymousViewModel.spark │ │ ├── ViewThatUsesApplicationLayout.spark │ │ ├── ViewThatUsesForeach.spark │ │ ├── ViewThatUsesFormatting.spark │ │ ├── ViewThatUsesHtmlEncoding.spark │ │ ├── ViewThatUsesNamespaces.spark │ │ ├── ViewThatUsesNullHtmlEncoding.spark │ │ ├── ViewThatUsesPartial.spark │ │ ├── ViewThatUsesPartialImplicitly.spark │ │ ├── ViewThatUsesTildeSubstitution.spark │ │ ├── ViewThatUsesTildeSubstitutionWithSparkReplace.spark │ │ ├── ViewThatUsesViewDataForViewBag.spark │ │ ├── ViewThatUsesViewModel.spark │ │ └── _Row.spark │ └── ViewModels/ │ └── FakeViewModel.cs └── tools/ └── xunit/ ├── HTML.xslt ├── NUnitXml.xslt ├── xUnit1.xslt ├── xunit.console.exe.config ├── xunit.console.x86.exe.config └── xunitmono.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org # top-most EditorConfig file root = true # Default settings: # A newline ending every file # Use 4 spaces as indentation [*] trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 # Xml project files [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] indent_size = 2 # Xml files [*.{xml,stylecop,resx,ruleset}] indent_size = 2 # Xml config files [*.{props,targets,config,nuspec}] indent_size = 2 # Shell scripts [*.sh] end_of_line = lf [*.{cmd,bat,ps1}] end_of_line = crlf ================================================ FILE: .gitattributes ================================================ * -crlf ================================================ FILE: .github/CONTRIBUTING.md ================================================ # How to contribute First of all, thank you for wanting to contribute to Nancy! We really appreciate all the awesome support we get from our community. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we have a chance of keeping on top of things. - [Making Changes](#making-changes) - [Handling Updates from Upstream/Master](#handling-updates-from-upstreammaster) - [Sending a Pull Request](#sending-a-pull-request) - [Style Guidelines](#style-guidelines) ## Making Changes 1. [Fork](http://help.github.com/forking/) on GitHub 1. Clone your fork locally 1. Configure the upstream repo (`git remote add upstream git://github.com/NancyFx/Nancy`) 1. Create a local branch (`git checkout -b myBranch`) 1. Work on your feature 1. Rebase if required (see below) 1. Push the branch up to GitHub (`git push origin myBranch`) 1. Send a Pull Request on GitHub You should **never** work on a clone of master, and you should **never** send a pull request from master - always from a branch. The reasons for this are detailed below. ### Handling Updates from Upstream/Master While you're working away in your branch it's quite possible that your upstream master (most likely the canonical NancyFx version) may be updated. If this happens you should: 1. [Stash](http://git-scm.com/book/en/Git-Tools-Stashing) any un-committed changes you need to 1. `git checkout master` 1. `git pull upstream master` 1. `git checkout myBranch` 1. `git rebase master myBranch` 1. `git push origin master` - (optional) this makes sure your remote master is up to date This ensures that your history is "clean" i.e. you have one branch off from master followed by your changes in a straight line. Failing to do this ends up with several "messy" merges in your history, which we don't want. This is the reason why you should always work in a branch and you should never be working in, or sending pull requests from, master. If you're working on a long running feature then you may want to do this quite often, rather than run the risk of potential merge issues further down the line. ### Sending a Pull Request While working on your feature you may well create several branches, which is fine, but before you send a pull request you should ensure that you have rebased back to a single "Feature branch". We care about your commits, and we care about your feature branch; but we don't care about how many or which branches you created while you were working on it :smile:. When you're ready to go you should confirm that you are up to date and rebased with upstream/master (see "Handling Updates from Upstream/Master" above), and then: 1. `git push origin myBranch` 1. Send a descriptive [Pull Request](http://help.github.com/pull-requests/) on GitHub - making sure you have selected the correct branch in the GitHub UI! 1. Wait for @TheCodeJunkie to merge your changes in and reformat all of your code because he has StyleCop OCD :wink:. And remember; **A pull-request with tests is a pull-request that's likely to be pulled in.** :grin: Bonus points if you document your feature in our [wiki](https://github.com/NancyFx/Nancy/wiki) once it has been pulled in ## Style Guidelines - Indent with 4 spaces, **not** tabs. - No underscore (`_`) prefix for member names. - Use `this` when accessing instance members, e.g. `this.Name = "TheCodeJunkie";`. - Use the `var` keyword unless the inferred type is not obvious. - Use the C# type aliases for types that have them, e.g. `int` instead of `Int32`, `string` instead of `String` etc. - Use meaningful names (no hungarian notation). - Wrap `if`, `else` and `using` blocks (or blocks in general, really) in curly braces, even if it's a single line. - Put `using` statements inside namespace. - Pay attention to whitespace and extra blank lines - Absolutely **no** regions > If you are a ReSharper user, you can make use of our `.DotSettings` file to ensure you cover as many of our style guidelines as possible. There may be some style guidelines which are not covered by the file, so please pay attention to the style of existing code. ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ### Prerequisites - [ ] I have written a descriptive issue title - [ ] I have verified that I am running the latest version of Nancy - [ ] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode - [ ] I have searched [open](https://github.com/NancyFx/Nancy/issues) and [closed](https://github.com/NancyFx/Nancy/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported ### Description ### Steps to Reproduce ### System Configuration - Nancy version: - Nancy host - [ ] Nancy.Hosting.Aspnet - [ ] Nancy.Hosting.Self - [ ] Nancy.Owin () - [ ] Other: - Other Nancy packages and versions: - Environment (Operating system, version and so on): - .NET Framework version: - Additional information: ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### Prerequisites - [ ] I have written a descriptive pull-request title - [ ] I have verified that there are no overlapping [pull-requests](https://github.com/NancyFx/Nancy/pulls) open - [ ] I have verified that I am following the Nancy [code style guidelines](https://github.com/NancyFx/Nancy/blob/45238076ad0b7f6ecabd6bae8469e30458d02efe/CONTRIBUTING.md#style-guidelines) - [ ] I have provided test coverage for my change (where applicable) ### Description ================================================ FILE: .gitignore ================================================ *.[Cc]ache *.csproj.user *.[Rr]e[Ss]harper* *.sln.cache *.suo *.user *.orig *.pidb *.ide *.userprefs /AssemblyInfo.cs .DS_Store deploy/ /build/ [Bb]in/ [Dd]ebug/ [Oo]bj/ [Rr]elease/ _[Rr]e[Ss]harper*/ *.docstates *.tss *.ncrunchproject *.ncrunchsolution *.dotCover src/_NCrunch_Nancy/ Thumbs.db .idea *.GhostDoc.xml Gemfile.lock .vs/ packages/ project.lock.json TestAssembly.dll [Tt]ools/Cake.* .vscode/ .dotnet/ [Tt]ools/Addins/ TestResults/ ================================================ FILE: .mailmap ================================================ Andreas Håkansson Andreas Håkansson Andreas Håkansson Andreas Håkansson Steven Robbins Steven Robbins Steven Robbins Brendan Forster Brendan Forster Jonathan Channon Jonathan Channon Brendan Tompkins ================================================ FILE: .travis.yml ================================================ language: csharp os: - linux - osx sudo: required dist: trusty mono: - 4.4.2 dotnet: 2.1.4 script: - ./build.sh --verbosity=minimal notifications: email: - nancy@nancyfx.org ================================================ FILE: Nancy.ruleset ================================================  ================================================ FILE: Nancy.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5734E9DC-CF67-4337-B28E-695280598B88}" ProjectSection(SolutionItems) = preProject src\Directory.Build.props = src\Directory.Build.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{AAB0D92A-7B90-478A-8360-3E6A20BD0B71}" ProjectSection(SolutionItems) = preProject NuGet.config = NuGet.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{AA791E28-7914-439A-B59A-580B8D8FE95D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{058BCE2A-8F81-4404-83D3-844CE030E513}" ProjectSection(SolutionItems) = preProject test\Directory.Build.props = test\Directory.Build.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Razor.Tests.Models", "test\Nancy.ViewEngines.Razor.Tests.Models\Nancy.ViewEngines.Razor.Tests.Models.csproj", "{0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Razor.Tests", "test\Nancy.ViewEngines.Razor.Tests\Nancy.ViewEngines.Razor.Tests.csproj", "{06B9857A-02A6-430D-B579-98A762C281D3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy", "src\Nancy\Nancy.csproj", "{ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Testing", "src\Nancy.Testing\Nancy.Testing.csproj", "{66119997-5949-45D6-9175-F1C7C599A8F5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Razor", "src\Nancy.ViewEngines.Razor\Nancy.ViewEngines.Razor.csproj", "{D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Authentication.Forms", "src\Nancy.Authentication.Forms\Nancy.Authentication.Forms.csproj", "{09F5EB90-1063-4338-A146-25F7BE8BFF43}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Markdown.Tests", "test\Nancy.ViewEngines.Markdown.Tests\Nancy.ViewEngines.Markdown.Tests.csproj", "{1CA8D3D3-B6A6-4006-AA96-671CB41D6391}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Markdown", "src\Nancy.ViewEngines.Markdown\Nancy.ViewEngines.Markdown.csproj", "{CCB5075B-C50A-4CD1-983A-2D51DC2430F4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.DotLiquid.Tests", "test\Nancy.ViewEngines.DotLiquid.Tests\Nancy.ViewEngines.DotLiquid.Tests.csproj", "{89C49FF5-9441-4DE8-A33F-1B2852401A54}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.DotLiquid", "src\Nancy.ViewEngines.DotLiquid\Nancy.ViewEngines.DotLiquid.csproj", "{9F16EBDF-4546-43C2-82B8-7D698F7A1E89}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Validation.FluentValidation.Tests", "test\Nancy.Validation.FluentValidation.Tests\Nancy.Validation.FluentValidation.Tests.csproj", "{0E9B466C-1151-4AE9-A55A-6377A25F8235}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Validation.FluentValidation", "src\Nancy.Validation.FluentValidation\Nancy.Validation.FluentValidation.csproj", "{33D5FB92-074E-4C13-BFD6-21184DD0013A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Validation.DataAnnotations.Tests", "test\Nancy.Validation.DataAnnotations.Tests\Nancy.Validation.DataAnnotations.Tests.csproj", "{EF3149F4-59C1-4896-989E-59D9D5F9050A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Validation.DataAnnotations", "src\Nancy.Validation.DataAnnotations\Nancy.Validation.DataAnnotations.csproj", "{FA2462FE-A439-48C5-976D-491F73C25CE1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Tests.Functional", "test\Nancy.Tests.Functional\Nancy.Tests.Functional.csproj", "{7944AA31-16BA-43A8-A9CB-2ED758518C9E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Tests", "test\Nancy.Tests\Nancy.Tests.csproj", "{9C383F09-8936-42ED-B9A6-ED3300AECB1C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Testing.Tests", "test\Nancy.Testing.Tests\Nancy.Testing.Tests.csproj", "{5E5AF23F-61F6-4B44-B682-2D141B54DF5C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Owin.Tests", "test\Nancy.Owin.Tests\Nancy.Owin.Tests.csproj", "{C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Owin", "src\Nancy.Owin\Nancy.Owin.csproj", "{23C68B10-36FD-41DE-AAA3-77BEDFD63945}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Metadata.Modules.Tests", "test\Nancy.Metadata.Modules.Tests\Nancy.Metadata.Modules.Tests.csproj", "{8302F65E-035F-45D7-B894-915B68B72711}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Metadata.Modules", "src\Nancy.Metadata.Modules\Nancy.Metadata.Modules.csproj", "{89DED7A5-7334-4852-A1FC-9266AD0EF388}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Hosting.Self.Tests", "test\Nancy.Hosting.Self.Tests\Nancy.Hosting.Self.Tests.csproj", "{8909AB97-EDD6-4AB2-B733-A543ACB957CA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Hosting.Self", "src\Nancy.Hosting.Self\Nancy.Hosting.Self.csproj", "{AA837BCA-A2D6-4F74-95BC-1F6A66146562}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Hosting.Aspnet.Tests", "test\Nancy.Hosting.Aspnet.Tests\Nancy.Hosting.Aspnet.Tests.csproj", "{53FB25E0-C8CB-4D65-8D94-6FA627BF9760}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Hosting.Aspnet", "src\Nancy.Hosting.Aspnet\Nancy.Hosting.Aspnet.csproj", "{833A65BC-5B37-468D-8BB4-2343CEFDFA5D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Encryption.MachineKey.Tests", "test\Nancy.Encryption.MachineKey.Tests\Nancy.Encryption.MachineKey.Tests.csproj", "{C42C352E-F3E2-41F6-A446-0CACDA5096AF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Encryption.MachineKey", "src\Nancy.Encryption.MachineKey\Nancy.Encryption.MachineKey.csproj", "{EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Embedded.Tests", "test\Nancy.Embedded.Tests\Nancy.Embedded.Tests.csproj", "{8A936050-5B84-4711-A235-2BA36A445B26}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Embedded", "src\Nancy.Embedded\Nancy.Embedded.csproj", "{8120B952-D9BC-450A-B691-C030177CD24D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Authentication.Forms.Tests", "test\Nancy.Authentication.Forms.Tests\Nancy.Authentication.Forms.Tests.csproj", "{E3C25A1C-2754-4134-8780-1F0A247D6850}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Authentication.Basic.Tests", "test\Nancy.Authentication.Basic.Tests\Nancy.Authentication.Basic.Tests.csproj", "{5323D32E-0CF2-41B6-AF91-37F744AC1554}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Authentication.Basic", "src\Nancy.Authentication.Basic\Nancy.Authentication.Basic.csproj", "{22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Spark", "src\Nancy.ViewEngines.Spark\Nancy.ViewEngines.Spark.csproj", "{0E546413-BBF8-40B3-8534-8D1D986AA888}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Razor.BuildProviders", "src\Nancy.ViewEngines.Razor.BuildProviders\Nancy.ViewEngines.Razor.BuildProviders.csproj", "{D92ADA88-361B-480A-81D2-AE1F7B2E0660}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Nustache", "src\Nancy.ViewEngines.Nustache\Nancy.ViewEngines.Nustache.csproj", "{4FB42026-81C2-47B3-99A4-751452E64814}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Authentication.Stateless", "src\Nancy.Authentication.Stateless\Nancy.Authentication.Stateless.csproj", "{EEEE504F-24AC-4932-9B28-F684AB20FB50}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.Demo.Hosting.Kestrel", "samples\Nancy.Demo.Hosting.Kestrel\Nancy.Demo.Hosting.Kestrel.csproj", "{3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nancy.ViewEngines.Spark.Tests", "test\Nancy.ViewEngines.Spark.Tests\Nancy.ViewEngines.Spark.Tests.csproj", "{08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|x64.ActiveCfg = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|x64.Build.0 = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|x86.ActiveCfg = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Debug|x86.Build.0 = Debug|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|Any CPU.Build.0 = Release|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|x64.ActiveCfg = Release|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|x64.Build.0 = Release|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|x86.ActiveCfg = Release|Any CPU {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC}.Release|x86.Build.0 = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|x64.ActiveCfg = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|x64.Build.0 = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|x86.ActiveCfg = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Debug|x86.Build.0 = Debug|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|Any CPU.Build.0 = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|x64.ActiveCfg = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|x64.Build.0 = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|x86.ActiveCfg = Release|Any CPU {06B9857A-02A6-430D-B579-98A762C281D3}.Release|x86.Build.0 = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|x64.ActiveCfg = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|x64.Build.0 = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|x86.ActiveCfg = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Debug|x86.Build.0 = Debug|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|Any CPU.Build.0 = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|x64.ActiveCfg = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|x64.Build.0 = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|x86.ActiveCfg = Release|Any CPU {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66}.Release|x86.Build.0 = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|Any CPU.Build.0 = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|x64.ActiveCfg = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|x64.Build.0 = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|x86.ActiveCfg = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Debug|x86.Build.0 = Debug|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|Any CPU.Build.0 = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|x64.ActiveCfg = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|x64.Build.0 = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|x86.ActiveCfg = Release|Any CPU {66119997-5949-45D6-9175-F1C7C599A8F5}.Release|x86.Build.0 = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|x64.ActiveCfg = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|x64.Build.0 = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|x86.ActiveCfg = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Debug|x86.Build.0 = Debug|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|Any CPU.Build.0 = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|x64.ActiveCfg = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|x64.Build.0 = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|x86.ActiveCfg = Release|Any CPU {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5}.Release|x86.Build.0 = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|Any CPU.Build.0 = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|x64.ActiveCfg = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|x64.Build.0 = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|x86.ActiveCfg = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Debug|x86.Build.0 = Debug|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|Any CPU.ActiveCfg = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|Any CPU.Build.0 = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|x64.ActiveCfg = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|x64.Build.0 = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|x86.ActiveCfg = Release|Any CPU {09F5EB90-1063-4338-A146-25F7BE8BFF43}.Release|x86.Build.0 = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|x64.ActiveCfg = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|x64.Build.0 = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|x86.ActiveCfg = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Debug|x86.Build.0 = Debug|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|Any CPU.Build.0 = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|x64.ActiveCfg = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|x64.Build.0 = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|x86.ActiveCfg = Release|Any CPU {1CA8D3D3-B6A6-4006-AA96-671CB41D6391}.Release|x86.Build.0 = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|x64.ActiveCfg = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|x64.Build.0 = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|x86.ActiveCfg = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Debug|x86.Build.0 = Debug|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|Any CPU.Build.0 = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|x64.ActiveCfg = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|x64.Build.0 = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|x86.ActiveCfg = Release|Any CPU {CCB5075B-C50A-4CD1-983A-2D51DC2430F4}.Release|x86.Build.0 = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|Any CPU.Build.0 = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|x64.ActiveCfg = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|x64.Build.0 = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|x86.ActiveCfg = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Debug|x86.Build.0 = Debug|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|Any CPU.ActiveCfg = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|Any CPU.Build.0 = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|x64.ActiveCfg = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|x64.Build.0 = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|x86.ActiveCfg = Release|Any CPU {89C49FF5-9441-4DE8-A33F-1B2852401A54}.Release|x86.Build.0 = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|x64.ActiveCfg = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|x64.Build.0 = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|x86.ActiveCfg = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Debug|x86.Build.0 = Debug|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|Any CPU.Build.0 = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|x64.ActiveCfg = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|x64.Build.0 = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|x86.ActiveCfg = Release|Any CPU {9F16EBDF-4546-43C2-82B8-7D698F7A1E89}.Release|x86.Build.0 = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|x64.ActiveCfg = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|x64.Build.0 = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|x86.ActiveCfg = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Debug|x86.Build.0 = Debug|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|Any CPU.Build.0 = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|x64.ActiveCfg = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|x64.Build.0 = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|x86.ActiveCfg = Release|Any CPU {0E9B466C-1151-4AE9-A55A-6377A25F8235}.Release|x86.Build.0 = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|Any CPU.Build.0 = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|x64.ActiveCfg = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|x64.Build.0 = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|x86.ActiveCfg = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Debug|x86.Build.0 = Debug|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|Any CPU.ActiveCfg = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|Any CPU.Build.0 = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|x64.ActiveCfg = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|x64.Build.0 = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|x86.ActiveCfg = Release|Any CPU {33D5FB92-074E-4C13-BFD6-21184DD0013A}.Release|x86.Build.0 = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|x64.ActiveCfg = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|x64.Build.0 = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|x86.ActiveCfg = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Debug|x86.Build.0 = Debug|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|Any CPU.Build.0 = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|x64.ActiveCfg = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|x64.Build.0 = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|x86.ActiveCfg = Release|Any CPU {EF3149F4-59C1-4896-989E-59D9D5F9050A}.Release|x86.Build.0 = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|x64.ActiveCfg = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|x64.Build.0 = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|x86.ActiveCfg = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Debug|x86.Build.0 = Debug|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|Any CPU.Build.0 = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|x64.ActiveCfg = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|x64.Build.0 = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|x86.ActiveCfg = Release|Any CPU {FA2462FE-A439-48C5-976D-491F73C25CE1}.Release|x86.Build.0 = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|x64.ActiveCfg = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|x64.Build.0 = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|x86.ActiveCfg = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Debug|x86.Build.0 = Debug|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|Any CPU.Build.0 = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|x64.ActiveCfg = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|x64.Build.0 = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|x86.ActiveCfg = Release|Any CPU {7944AA31-16BA-43A8-A9CB-2ED758518C9E}.Release|x86.Build.0 = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|x64.ActiveCfg = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|x64.Build.0 = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|x86.ActiveCfg = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Debug|x86.Build.0 = Debug|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|Any CPU.Build.0 = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|x64.ActiveCfg = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|x64.Build.0 = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|x86.ActiveCfg = Release|Any CPU {9C383F09-8936-42ED-B9A6-ED3300AECB1C}.Release|x86.Build.0 = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|x64.ActiveCfg = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|x64.Build.0 = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|x86.ActiveCfg = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Debug|x86.Build.0 = Debug|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|Any CPU.Build.0 = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|x64.ActiveCfg = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|x64.Build.0 = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|x86.ActiveCfg = Release|Any CPU {5E5AF23F-61F6-4B44-B682-2D141B54DF5C}.Release|x86.Build.0 = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|Any CPU.Build.0 = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|x64.ActiveCfg = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|x64.Build.0 = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|x86.ActiveCfg = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Debug|x86.Build.0 = Debug|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|Any CPU.ActiveCfg = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|Any CPU.Build.0 = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|x64.ActiveCfg = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|x64.Build.0 = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|x86.ActiveCfg = Release|Any CPU {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522}.Release|x86.Build.0 = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|Any CPU.Build.0 = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|x64.ActiveCfg = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|x64.Build.0 = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|x86.ActiveCfg = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Debug|x86.Build.0 = Debug|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|Any CPU.ActiveCfg = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|Any CPU.Build.0 = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|x64.ActiveCfg = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|x64.Build.0 = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|x86.ActiveCfg = Release|Any CPU {23C68B10-36FD-41DE-AAA3-77BEDFD63945}.Release|x86.Build.0 = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|Any CPU.Build.0 = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|x64.ActiveCfg = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|x64.Build.0 = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|x86.ActiveCfg = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Debug|x86.Build.0 = Debug|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|Any CPU.ActiveCfg = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|Any CPU.Build.0 = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|x64.ActiveCfg = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|x64.Build.0 = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|x86.ActiveCfg = Release|Any CPU {8302F65E-035F-45D7-B894-915B68B72711}.Release|x86.Build.0 = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|Any CPU.Build.0 = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|x64.ActiveCfg = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|x64.Build.0 = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|x86.ActiveCfg = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Debug|x86.Build.0 = Debug|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|Any CPU.ActiveCfg = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|Any CPU.Build.0 = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|x64.ActiveCfg = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|x64.Build.0 = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|x86.ActiveCfg = Release|Any CPU {89DED7A5-7334-4852-A1FC-9266AD0EF388}.Release|x86.Build.0 = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|Any CPU.Build.0 = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|x64.ActiveCfg = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|x64.Build.0 = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|x86.ActiveCfg = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Debug|x86.Build.0 = Debug|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|Any CPU.ActiveCfg = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|Any CPU.Build.0 = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|x64.ActiveCfg = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|x64.Build.0 = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|x86.ActiveCfg = Release|Any CPU {8909AB97-EDD6-4AB2-B733-A543ACB957CA}.Release|x86.Build.0 = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|x64.ActiveCfg = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|x64.Build.0 = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|x86.ActiveCfg = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Debug|x86.Build.0 = Debug|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|Any CPU.Build.0 = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|x64.ActiveCfg = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|x64.Build.0 = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|x86.ActiveCfg = Release|Any CPU {AA837BCA-A2D6-4F74-95BC-1F6A66146562}.Release|x86.Build.0 = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|Any CPU.Build.0 = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|x64.ActiveCfg = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|x64.Build.0 = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|x86.ActiveCfg = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Debug|x86.Build.0 = Debug|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|Any CPU.ActiveCfg = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|Any CPU.Build.0 = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|x64.ActiveCfg = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|x64.Build.0 = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|x86.ActiveCfg = Release|Any CPU {53FB25E0-C8CB-4D65-8D94-6FA627BF9760}.Release|x86.Build.0 = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|Any CPU.Build.0 = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|x64.ActiveCfg = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|x64.Build.0 = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|x86.ActiveCfg = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Debug|x86.Build.0 = Debug|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|Any CPU.ActiveCfg = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|Any CPU.Build.0 = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|x64.ActiveCfg = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|x64.Build.0 = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|x86.ActiveCfg = Release|Any CPU {833A65BC-5B37-468D-8BB4-2343CEFDFA5D}.Release|x86.Build.0 = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|Any CPU.Build.0 = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|x64.ActiveCfg = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|x64.Build.0 = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|x86.ActiveCfg = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Debug|x86.Build.0 = Debug|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|Any CPU.Build.0 = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|x64.ActiveCfg = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|x64.Build.0 = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|x86.ActiveCfg = Release|Any CPU {C42C352E-F3E2-41F6-A446-0CACDA5096AF}.Release|x86.Build.0 = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|Any CPU.Build.0 = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|x64.ActiveCfg = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|x64.Build.0 = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|x86.ActiveCfg = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Debug|x86.Build.0 = Debug|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|Any CPU.Build.0 = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|x64.ActiveCfg = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|x64.Build.0 = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|x86.ActiveCfg = Release|Any CPU {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D}.Release|x86.Build.0 = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|x64.ActiveCfg = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|x64.Build.0 = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|x86.ActiveCfg = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Debug|x86.Build.0 = Debug|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|Any CPU.Build.0 = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|x64.ActiveCfg = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|x64.Build.0 = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|x86.ActiveCfg = Release|Any CPU {8A936050-5B84-4711-A235-2BA36A445B26}.Release|x86.Build.0 = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|x64.ActiveCfg = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|x64.Build.0 = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|x86.ActiveCfg = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Debug|x86.Build.0 = Debug|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|Any CPU.Build.0 = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|x64.ActiveCfg = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|x64.Build.0 = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|x86.ActiveCfg = Release|Any CPU {8120B952-D9BC-450A-B691-C030177CD24D}.Release|x86.Build.0 = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|x64.ActiveCfg = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|x64.Build.0 = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|x86.ActiveCfg = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Debug|x86.Build.0 = Debug|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|Any CPU.Build.0 = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|x64.ActiveCfg = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|x64.Build.0 = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|x86.ActiveCfg = Release|Any CPU {E3C25A1C-2754-4134-8780-1F0A247D6850}.Release|x86.Build.0 = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|Any CPU.Build.0 = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|x64.ActiveCfg = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|x64.Build.0 = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|x86.ActiveCfg = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Debug|x86.Build.0 = Debug|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|Any CPU.ActiveCfg = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|Any CPU.Build.0 = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|x64.ActiveCfg = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|x64.Build.0 = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|x86.ActiveCfg = Release|Any CPU {5323D32E-0CF2-41B6-AF91-37F744AC1554}.Release|x86.Build.0 = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|x64.ActiveCfg = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|x64.Build.0 = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|x86.ActiveCfg = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Debug|x86.Build.0 = Debug|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|Any CPU.Build.0 = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|x64.ActiveCfg = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|x64.Build.0 = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|x86.ActiveCfg = Release|Any CPU {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD}.Release|x86.Build.0 = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|x64.ActiveCfg = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|x64.Build.0 = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|x86.ActiveCfg = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Debug|x86.Build.0 = Debug|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|Any CPU.Build.0 = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|x64.ActiveCfg = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|x64.Build.0 = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|x86.ActiveCfg = Release|Any CPU {0E546413-BBF8-40B3-8534-8D1D986AA888}.Release|x86.Build.0 = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|Any CPU.Build.0 = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|x64.ActiveCfg = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|x64.Build.0 = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|x86.ActiveCfg = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Debug|x86.Build.0 = Debug|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|Any CPU.ActiveCfg = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|Any CPU.Build.0 = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|x64.ActiveCfg = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|x64.Build.0 = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|x86.ActiveCfg = Release|Any CPU {D92ADA88-361B-480A-81D2-AE1F7B2E0660}.Release|x86.Build.0 = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|x64.ActiveCfg = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|x64.Build.0 = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|x86.ActiveCfg = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Debug|x86.Build.0 = Debug|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|Any CPU.Build.0 = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|x64.ActiveCfg = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|x64.Build.0 = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|x86.ActiveCfg = Release|Any CPU {4FB42026-81C2-47B3-99A4-751452E64814}.Release|x86.Build.0 = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|x64.ActiveCfg = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|x64.Build.0 = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|x86.ActiveCfg = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Debug|x86.Build.0 = Debug|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|Any CPU.Build.0 = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|x64.ActiveCfg = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|x64.Build.0 = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|x86.ActiveCfg = Release|Any CPU {EEEE504F-24AC-4932-9B28-F684AB20FB50}.Release|x86.Build.0 = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|x64.ActiveCfg = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|x64.Build.0 = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|x86.ActiveCfg = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Debug|x86.Build.0 = Debug|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|Any CPU.Build.0 = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|x64.ActiveCfg = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|x64.Build.0 = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|x86.ActiveCfg = Release|Any CPU {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD}.Release|x86.Build.0 = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|x64.ActiveCfg = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|x64.Build.0 = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|x86.ActiveCfg = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Debug|x86.Build.0 = Debug|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|Any CPU.Build.0 = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|x64.ActiveCfg = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|x64.Build.0 = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|x86.ActiveCfg = Release|Any CPU {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {0E7E06C1-C223-4975-A9DB-D338EBF0A9BC} = {058BCE2A-8F81-4404-83D3-844CE030E513} {06B9857A-02A6-430D-B579-98A762C281D3} = {058BCE2A-8F81-4404-83D3-844CE030E513} {ADAB2C6B-10FE-4B31-BCD2-E2EFA69D5A66} = {5734E9DC-CF67-4337-B28E-695280598B88} {66119997-5949-45D6-9175-F1C7C599A8F5} = {5734E9DC-CF67-4337-B28E-695280598B88} {D1D2622E-ACF7-479C-ABC8-E1B94E41D9D5} = {5734E9DC-CF67-4337-B28E-695280598B88} {09F5EB90-1063-4338-A146-25F7BE8BFF43} = {5734E9DC-CF67-4337-B28E-695280598B88} {1CA8D3D3-B6A6-4006-AA96-671CB41D6391} = {058BCE2A-8F81-4404-83D3-844CE030E513} {CCB5075B-C50A-4CD1-983A-2D51DC2430F4} = {5734E9DC-CF67-4337-B28E-695280598B88} {89C49FF5-9441-4DE8-A33F-1B2852401A54} = {058BCE2A-8F81-4404-83D3-844CE030E513} {9F16EBDF-4546-43C2-82B8-7D698F7A1E89} = {5734E9DC-CF67-4337-B28E-695280598B88} {0E9B466C-1151-4AE9-A55A-6377A25F8235} = {058BCE2A-8F81-4404-83D3-844CE030E513} {33D5FB92-074E-4C13-BFD6-21184DD0013A} = {5734E9DC-CF67-4337-B28E-695280598B88} {EF3149F4-59C1-4896-989E-59D9D5F9050A} = {058BCE2A-8F81-4404-83D3-844CE030E513} {FA2462FE-A439-48C5-976D-491F73C25CE1} = {5734E9DC-CF67-4337-B28E-695280598B88} {7944AA31-16BA-43A8-A9CB-2ED758518C9E} = {058BCE2A-8F81-4404-83D3-844CE030E513} {9C383F09-8936-42ED-B9A6-ED3300AECB1C} = {058BCE2A-8F81-4404-83D3-844CE030E513} {5E5AF23F-61F6-4B44-B682-2D141B54DF5C} = {058BCE2A-8F81-4404-83D3-844CE030E513} {C0A85A7D-9D7F-4A6C-BA33-9E79050CF522} = {058BCE2A-8F81-4404-83D3-844CE030E513} {23C68B10-36FD-41DE-AAA3-77BEDFD63945} = {5734E9DC-CF67-4337-B28E-695280598B88} {8302F65E-035F-45D7-B894-915B68B72711} = {058BCE2A-8F81-4404-83D3-844CE030E513} {89DED7A5-7334-4852-A1FC-9266AD0EF388} = {5734E9DC-CF67-4337-B28E-695280598B88} {8909AB97-EDD6-4AB2-B733-A543ACB957CA} = {058BCE2A-8F81-4404-83D3-844CE030E513} {AA837BCA-A2D6-4F74-95BC-1F6A66146562} = {5734E9DC-CF67-4337-B28E-695280598B88} {53FB25E0-C8CB-4D65-8D94-6FA627BF9760} = {058BCE2A-8F81-4404-83D3-844CE030E513} {833A65BC-5B37-468D-8BB4-2343CEFDFA5D} = {5734E9DC-CF67-4337-B28E-695280598B88} {C42C352E-F3E2-41F6-A446-0CACDA5096AF} = {058BCE2A-8F81-4404-83D3-844CE030E513} {EF313B39-68CF-45D1-9094-BDDCCAC9DB2D} = {5734E9DC-CF67-4337-B28E-695280598B88} {8A936050-5B84-4711-A235-2BA36A445B26} = {058BCE2A-8F81-4404-83D3-844CE030E513} {8120B952-D9BC-450A-B691-C030177CD24D} = {5734E9DC-CF67-4337-B28E-695280598B88} {E3C25A1C-2754-4134-8780-1F0A247D6850} = {058BCE2A-8F81-4404-83D3-844CE030E513} {5323D32E-0CF2-41B6-AF91-37F744AC1554} = {058BCE2A-8F81-4404-83D3-844CE030E513} {22E99FEC-8DFC-4E87-90EE-BC7F6E988DAD} = {5734E9DC-CF67-4337-B28E-695280598B88} {0E546413-BBF8-40B3-8534-8D1D986AA888} = {5734E9DC-CF67-4337-B28E-695280598B88} {D92ADA88-361B-480A-81D2-AE1F7B2E0660} = {5734E9DC-CF67-4337-B28E-695280598B88} {4FB42026-81C2-47B3-99A4-751452E64814} = {5734E9DC-CF67-4337-B28E-695280598B88} {EEEE504F-24AC-4932-9B28-F684AB20FB50} = {5734E9DC-CF67-4337-B28E-695280598B88} {3AF8D59E-D10A-46DE-97A6-0A6C156F22CD} = {AA791E28-7914-439A-B59A-580B8D8FE95D} {08C1D823-E8E1-4D86-8F73-A9F3AE8FEFC7} = {058BCE2A-8F81-4404-83D3-844CE030E513} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {781C77FF-8818-4AF3-BCAF-C82B52FFF0C0} EndGlobalSection EndGlobal ================================================ FILE: Nancy.sln.DotSettings ================================================  True WARNING WARNING WARNING WARNING WARNING WARNING <?xml version="1.0" encoding="utf-16"?><Profile name="NancyStandard"><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSShortenReferences>True</CSShortenReferences><CSReorderTypeMembers>True</CSReorderTypeMembers><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /></Profile> Required Required Required Required Required Required Required Required All False NEXT_LINE 1 1 1 1 1 0 ALWAYS_ADD ALWAYS_ADD ALWAYS_ADD ALWAYS_ADD ALWAYS_ADD ALWAYS_ADD NEXT_LINE 1 1 True ALWAYS_USE LINE_BREAK False True False False True False True True <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="Aa_bb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> True True True <data><IncludeFilters /><ExcludeFilters /></data> <data /> ================================================ FILE: NuGet.config ================================================ ================================================ FILE: README.md ================================================ # ** Announcement ** - Nancy is no longer being maintained! We would like to thank all the thousands of users of Nancy, all the people who wrote blog posts, conference speakers, video producers and those that spread the word of Nancy. We would like to thank the 150+ contributors to Nancy that made it what it became, without you the work would have been much harder and opportunities missed. We would like to thank [VQ](http://www.vqcomms.com) for financially sponsoring our open source efforts. We would like to thank the core contributors to Nancy [@jchannon](https://github.com/jchannon), [@khellang](https://github.com/khellang), [@damianh](https://github.com/damianh), [@phillip-haydon](https://github.com/phillip-haydon), [@prabirshrestha](https://github.com/prabirshrestha), [@horsdal](https://github.com/horsdal) for working hard into the nights coding, testing and writing docs but most importantly the founders of Nancy itself [@thecodejunkie](https://github.com/thecodejunkie) and [@grumpydev](https://github.com/grumpydev) whose vision made Nancy what it was, a fun, performant and enjoyable web framework. ## Support We understand that organisations may have services and products that still depend on Nancy in production. A couple of members of the team can offer a support, maintenance, migration services on commercial terms. Please reach out to [nancyfx.help@gmail.com](mailto:nancyfx.help@gmail.com) to discuss options. ## Forking Nancy's licence is permissible so we encourage forking if you need to perform maintenance. However, the logos and name are copyright to Andreas Håkansson and Steven Robbins and are not for re-use or editing. Please see full licence information [here](https://github.com/NancyFx/Nancy.Portfolio/blob/master/license.txt) ------------------------------------------------------------------------------------------------------------------------------------------ # Meet Nancy [![NuGet Version](http://img.shields.io/nuget/v/Nancy.svg?style=flat)](https://www.nuget.org/packages/Nancy/) [![Slack Status](http://slack.nancyfx.org/badge.svg)](http://slack.nancyfx.org) Nancy is a lightweight, low-ceremony, framework for building HTTP based services on .NET Framework/Core and [Mono](http://mono-project.com). The goal of the framework is to stay out of the way as much as possible and provide a super-duper-happy-path to all interactions. Nancy is designed to handle `DELETE`, `GET`, `HEAD`, `OPTIONS`, `POST`, `PUT` and `PATCH` requests and provides a simple, elegant, [Domain Specific Language (DSL)](http://en.wikipedia.org/wiki/Domain-specific_language) for returning a response with just a couple of keystrokes, leaving you with more time to focus on the important bits.. **your** code and **your** application. Write your application ```csharp public class Module : NancyModule { public Module() { Get("/greet/{name}", x => { return string.Concat("Hello ", x.name); }); } } ``` Compile, run and enjoy the simple, elegant design! ## Features * Built from the bottom up, not simply a DSL on top of an existing framework. Removing limitations and feature hacks of an underlying framework, as well as the need to reference more assemblies than you need. _keep it light_ * Run anywhere. Nancy is not built on any specific hosting technology can be run anywhere. Out of the box, Nancy supports running on ASP.NET/IIS, WCF, Self-hosting and any [OWIN](http://owin.org) * Ultra lightweight action declarations for GET, HEAD, PUT, POST, DELETE, OPTIONS and PATCH requests * View engine integration (Razor, Spark, dotLiquid, our own SuperSimpleViewEngine and many more) * Powerful request path matching that includes advanced parameter capabilities. The path matching strategy can be replaced with custom implementations to fit your exact needs * Easy response syntax, enabling you to return things like int, string, HttpStatusCode and Action elements without having to explicitly cast or wrap your response - you just return it and Nancy _will_ do the work for you * A powerful, light-weight, testing framework to help you verify the behavior of your application * Content negotiation * And much, much more ## The super-duper-happy-path The "super-duper-happy-path" (or SDHP if you’re ‘down with the kids’ ;-)) is a phrase we coined to describe the ethos of Nancy; and providing the “super-duper-happy-path” experience is something we strive for in all of our APIs. While it’s hard to pin down exactly what it is, it’s a very emotive term after all, but the basic ideas behind it are: * “It just works” - you should be able to pick things up and use them without any mucking about. Added a new module? That’s automatically discovered for you. Brought in a new View Engine? All wired up and ready to go without you having to do anything else. Even if you add a new dependency to your module, by default we’ll locate that and inject it for you - no configuration required. * “Easily customisable” - even though “it just works”, there shouldn’t be any barriers that get in the way of customisation should you want to work the way you want to work with the components that you want to use. Want to use another container? No problem! Want to tweak the way routes are selected? Go ahead! Through our bootstrapper approach all of these things should be a piece of cake. * “Low ceremony” - the amount of “Nancy code” you should need in your application should be minimal. The important part of any Nancy application is your code - our code should get out of your way and let you get on with building awesome applications. As a testament to this it’s actually possible to fit a functional Nancy application into a single Tweet :-) * “Low friction” - when building software with Nancy the APIs should help you get where you want to go, rather than getting in your way. Naming should be obvious, required configuration should be minimal, but power and extensibility should still be there when you need it. Above all, creating an application with Nancy should be a pleasure, and hopefully fun! But without sacrificing the power or extensibility that you may need as your application grows. ## Getting started As a start several working samples are provided in the `/sample` directory. Simply run the build script `build.ps1` (for Windows PowerShell) or `build.sh` (for \*nix-Bash) first. ## Community Nancy followers can be found on Slack [NancyFx team](http://nancyfx.slack.com). You can also find Nancy on Twitter using the #NancyFx hashtag. ## Help out There are many ways you can contribute to Nancy. Like most open-source software projects, contributing code is just one of many outlets where you can help improve. Some of the things that you could help out with in Nancy are: * Documentation (both code and features) * Bug reports * Bug fixes * Feature requests * Feature implementations * Test coverage * Code quality * Sample applications ## Continuous integration builds | Platform | Status | |-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| | AppVeyor (.NET & .NET Core) | [![Build Status](https://ci.appveyor.com/api/projects/status/mpd9lbxvithu16vg/branch/master?svg=true)](https://ci.appveyor.com/project/NancyFx/nancy) | | Travis (Mono) | [![Build Status](https://travis-ci.org/NancyFx/Nancy.png?branch=master)](https://travis-ci.org/NancyFx/Nancy) | To get build artifacts of latest `master`, please use our [MyGet feed](https://www.myget.org/gallery/nancyfx) ## Contributors Nancy is not a one man project and many of the features that are available would not have been possible without the awesome contributions from the community! For a full list of contributors, please see [the website](http://www.nancyfx.org/contribs.html). ## Code of Conduct This project has adopted the code of conduct defined by the [Contributor Covenant](http://contributor-covenant.org/) to clarify expected behavior in our community. For more information see the [.NET Foundation Code of Conduct](http://www.dotnetfoundation.org/code-of-conduct). ## Contribution License Agreement Contributing to Nancy requires you to sign a [contribution license agreement](https://cla2.dotnetfoundation.org/) (CLA) for anything other than a trivial change. By signing the contribution license agreement, the community is free to use your contribution to .NET Foundation projects. ## .NET Foundation This project is supported by the [.NET Foundation](http://www.dotnetfoundation.org). ## Copyright Copyright © 2010 Andreas Håkansson, Steven Robbins and contributors ## License Nancy is licensed under [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form"). Refer to license.txt for more information. ================================================ FILE: SharedAssemblyInfo.cs ================================================ using System.Runtime.InteropServices; using System.Reflection; [assembly: AssemblyTitle("Nancy")] [assembly: AssemblyDescription("A Sinatra inspired web framework for the .NET platform")] [assembly: AssemblyCompany("Nancy")] [assembly: AssemblyProduct("Nancy")] [assembly: AssemblyCopyright("Copyright (C) Andreas Hakansson, Steven Robbins and contributors")] [assembly: AssemblyVersion("2.0.0")] [assembly: AssemblyInformationalVersion("2.0.0-alpha")] ================================================ FILE: appveyor.yml ================================================ image: Visual Studio 2017 version: 2.0.0-ci000{build} configuration: Release cache: C:\Users\appveyor\.nuget\packages nuget: disable_publish_on_pr: true pull_requests: do_not_increment_build_number: true install: - set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% build_script: - ps: .\build.ps1 -verbosity=minimal artifacts: - path: build\nuget\*.nupkg name: NuGet deploy: - provider: NuGet server: https://www.myget.org/F/nancyfx/api/v2/package api_key: secure: +D460p+eBTZg/1k7YpDwGp8G74VhOhjxp62EypsjVyul8DT/6B8o3taDbQCZCJ6+ skip_symbols: true on: branch: master test: off ================================================ FILE: build.cake ================================================ // Usings using System.Text.RegularExpressions; // Arguments var target = Argument("target", "Default"); var source = Argument("source", null); var apiKey = Argument("apikey", null); var version = Argument("targetversion", "2.0.0-pre" + (EnvironmentVariable("APPVEYOR_BUILD_NUMBER") ?? "0")); var nogit = Argument("nogit", false); // Variables var configuration = "Release"; var fullFrameworkTarget = "net452"; var netStandardTarget = "netstandard2.0"; var netCoreTarget = "netcoreapp2.0"; // Directories var output = Directory("build"); var outputBinaries = output + Directory("binaries"); var outputBinariesNet452 = outputBinaries + Directory(fullFrameworkTarget); var outputBinariesNetstandard = outputBinaries + Directory(netStandardTarget); var outputPackages = output + Directory("packages"); var outputNuGet = output + Directory("nuget"); /* / TASK DEFINITIONS */ Task("Default") .IsDependentOn("Test") .IsDependentOn("Update-Version") .IsDependentOn("Package-NuGet"); Task("Clean") .Does(() => { CleanDirectories(new DirectoryPath[] { output, outputBinaries, outputPackages, outputNuGet, outputBinariesNet452, outputBinariesNetstandard }); CleanDirectories("./src/**/" + configuration); CleanDirectories("./test/**/" + configuration); CleanDirectories("./samples/**/" + configuration); }); Task("Compile") .Description("Builds all the projects in the solution") .IsDependentOn("Clean") .IsDependentOn("Restore-NuGet-Packages") .Does(() => { var projects = GetFiles("./**/*.csproj") - GetFiles("./samples/**/*.csproj"); if (projects.Count == 0) { throw new CakeException("Unable to find any projects to build."); } foreach(var project in projects) { var content = System.IO.File.ReadAllText(project.FullPath, Encoding.UTF8); if (IsRunningOnUnix() && content.Contains(">" + fullFrameworkTarget + "<")) { Information(project.GetFilename() + " only supports " +fullFrameworkTarget + " and cannot be built on *nix. Skipping."); continue; } DotNetCoreBuild(project.GetDirectory().FullPath, new DotNetCoreBuildSettings { ArgumentCustomization = args => { if (IsRunningOnUnix()) { args.Append(string.Concat("-f ", project.GetDirectory().GetDirectoryName().Contains(".Tests") ? netCoreTarget : netStandardTarget)); } return args; }, Configuration = configuration }); } }); Task("Package") .Description("Zips up the built binaries for easy distribution") .IsDependentOn("Publish") .Does(() => { var package = outputPackages + File("Nancy-Latest.zip"); var files = GetFiles(outputBinaries.Path.FullPath + "/**/*"); Zip(outputBinaries, package, files); }); Task("Package-NuGet") .Description("Generates NuGet packages for each project") .Does(() => { foreach(var project in GetFiles("./src/**/*.csproj")) { Information("Packaging " + project.GetFilename().FullPath); var content = System.IO.File.ReadAllText(project.FullPath, Encoding.UTF8); if (IsRunningOnUnix() && content.Contains(">" + fullFrameworkTarget + "<")) { Information(project.GetFilename() + " only supports " +fullFrameworkTarget + " and cannot be packaged on *nix. Skipping."); continue; } DotNetCorePack(project.GetDirectory().FullPath, new DotNetCorePackSettings { Configuration = configuration, OutputDirectory = outputNuGet }); } }); Task("Publish") .Description("Gathers output files and copies them to the output folder") .IsDependentOn("Compile") .Does(() => { // Copy net452 binaries. CopyFiles(GetFiles("./src/**/bin/" + configuration + "/" + fullFrameworkTarget + "/*.dll") + GetFiles("./src/**/bin/" + configuration + "/" + fullFrameworkTarget + "/*.xml") + GetFiles("./src/**/bin/" + configuration + "/" + fullFrameworkTarget + "/*.pdb") + GetFiles("./src/**/*.ps1"), outputBinariesNet452); // Copy netstandard binaries. CopyFiles(GetFiles("./src/**/bin/" + configuration + "/" + netStandardTarget + "/*.dll") + GetFiles("./src/**/bin/" + configuration + "/" + netStandardTarget + "/*.xml") + GetFiles("./src/**/bin/" + configuration + "/" + netStandardTarget + "/*.pdb") + GetFiles("./src/**/*.ps1"), outputBinariesNetstandard); }); Task("Publish-NuGet") .Description("Pushes the nuget packages in the nuget folder to a NuGet source. Also publishes the packages into the feeds.") .Does(() => { if(string.IsNullOrWhiteSpace(apiKey)) { throw new CakeException("No NuGet API key provided. You need to pass in --apikey=\"xyz\""); } var packages = GetFiles(outputNuGet.Path.FullPath + "/*.nupkg") - GetFiles(outputNuGet.Path.FullPath + "/*.symbols.nupkg"); foreach(var package in packages) { NuGetPush(package, new NuGetPushSettings { Source = source, ApiKey = apiKey }); } }); Task("Prepare-Release") .IsDependentOn("Update-Version") .Does(() => { // Add foreach (var file in GetFiles("./src/**/*.csproj")) { if (nogit) { Information("git " + string.Format("add {0}", file.FullPath)); } else { StartProcess("git", new ProcessSettings { Arguments = string.Format("add {0}", file.FullPath) }); } } // Commit if (nogit) { Information("git " + string.Format("commit -m \"Updated version to {0}\"", version)); } else { StartProcess("git", new ProcessSettings { Arguments = string.Format("commit -m \"Updated version to {0}\"", version) }); } // Tag if (nogit) { Information("git " + string.Format("tag \"v{0}\"", version)); } else { StartProcess("git", new ProcessSettings { Arguments = string.Format("tag \"v{0}\"", version) }); } //Push if (nogit) { Information("git push origin master"); Information("git push --tags"); } else { StartProcess("git", new ProcessSettings { Arguments = "push origin master" }); StartProcess("git", new ProcessSettings { Arguments = "push --tags" }); } }); Task("Restore-NuGet-Packages") .Description("Restores NuGet packages for all projects") .Does(() => { DotNetCoreRestore(new DotNetCoreRestoreSettings { ArgumentCustomization = args => { args.Append("--verbosity minimal"); return args; } }); }); Task("Tag") .Description("Tags the current release") .Does(() => { StartProcess("git", new ProcessSettings { Arguments = string.Format("tag \"v{0}\"", version) }); }); Task("Test") .Description("Executes unit tests for all projects") .IsDependentOn("Compile") .Does(() => { /* Exclude Nancy.ViewEngines.Spark.Tests from test execution until their problem with duplicate assembly references (if the same assembly exists more than once in the application domain, it fails to compile the views) has been fixed. */ var projects = GetFiles("./test/**/*.csproj") - GetFiles("./test/Nancy.ViewEngines.Spark.Tests/Nancy.ViewEngines.Spark.Tests.csproj"); if (projects.Count == 0) { throw new CakeException("Unable to find any projects to test."); } foreach(var project in projects) { var content = System.IO.File.ReadAllText(project.FullPath, Encoding.UTF8); if (IsRunningOnUnix() && content.Contains(">" + fullFrameworkTarget + "<")) { Information(project.GetFilename() + " only supports " +fullFrameworkTarget + " and tests cannot be executed on *nix. Skipping."); continue; } var settings = new ProcessSettings { Arguments = string.Concat("xunit -configuration ", configuration, " -nobuild"), WorkingDirectory = project.GetDirectory() }; if (IsRunningOnUnix()) { settings.Arguments.Append(string.Concat("-framework ", netCoreTarget)); } Information("Executing tests for " + project.GetFilename() + " with arguments: " + settings.Arguments.Render()); if (StartProcess("dotnet", settings) != 0) { throw new CakeException("One or more tests failed during execution of: " + project.GetFilename()); } } }); Task("Update-Version") .Does(() => { Information("Setting version to " + version); if(string.IsNullOrWhiteSpace(version)) { throw new CakeException("No version specified! You need to pass in --targetversion=\"x.y.z\""); } var file = MakeAbsolute(File("./src/Directory.Build.props")); Information(file.FullPath); var project = System.IO.File.ReadAllText(file.FullPath, Encoding.UTF8); var projectVersion = new Regex(@".+<\/Version>"); project = projectVersion.Replace(project, string.Concat("", version, "")); System.IO.File.WriteAllText(file.FullPath, project, Encoding.UTF8); }); /* / RUN BUILD TARGET */ RunTarget(target); ================================================ FILE: build.ps1 ================================================ $CakeVersion = "0.24.0" $DotNetVersion = select-string -Path .\global.json -Pattern '[\d]\.[\d]\.[\d]' | % {$_.Matches} | % {$_.Value }; $DotNetInstallerUri = "https://raw.githubusercontent.com/dotnet/cli/v$DotNetVersion/scripts/obtain/dotnet-install.ps1"; # Make sure tools folder exists $PSScriptRoot = $pwd $ToolPath = Join-Path $PSScriptRoot "tools" if (!(Test-Path $ToolPath)) { Write-Verbose "Creating tools directory..." New-Item -Path $ToolPath -Type directory | out-null } ########################################################################### # INSTALL .NET CORE CLI ########################################################################### Function Remove-PathVariable([string]$VariableToRemove) { $path = [Environment]::GetEnvironmentVariable("PATH", "User") if ($path -ne $null) { $newItems = $path.Split(';', [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "User") } $path = [Environment]::GetEnvironmentVariable("PATH", "Process") if ($path -ne $null) { $newItems = $path.Split(';', [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "Process") } } # Get .NET Core CLI path if installed. $FoundDotNetCliVersion = $null; if (Get-Command dotnet -ErrorAction SilentlyContinue) { $FoundDotNetCliVersion = dotnet --version; } if($FoundDotNetCliVersion -ne $DotNetVersion) { $InstallPath = Join-Path $PSScriptRoot ".dotnet" if (!(Test-Path $InstallPath)) { mkdir -Force $InstallPath | Out-Null; } (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, "$InstallPath\dotnet-install.ps1"); & $InstallPath\dotnet-install.ps1 -Channel Current -Version $DotNetVersion -InstallDir $InstallPath; Remove-PathVariable "$InstallPath" $env:PATH = "$InstallPath;$env:PATH" $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 & dotnet --info } ########################################################################### # INSTALL CAKE ########################################################################### Add-Type -AssemblyName System.IO.Compression.FileSystem Function Unzip { param([string]$zipfile, [string]$outpath) [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) } # Make sure Cake has been installed. $CakePath = Join-Path $ToolPath "Cake.$CakeVersion/Cake.exe" if (!(Test-Path $CakePath)) { Write-Host "Installing Cake..." (New-Object System.Net.WebClient).DownloadFile("https://www.nuget.org/api/v2/package/Cake/$CakeVersion", "$ToolPath\Cake.zip") Unzip "$ToolPath\Cake.zip" "$ToolPath/Cake.$CakeVersion" Remove-Item "$ToolPath\Cake.zip" } ########################################################################### # RUN BUILD SCRIPT ########################################################################### & "$CakePath" $args exit $LASTEXITCODE ================================================ FILE: build.sh ================================================ #!/usr/bin/env bash # Define directories. SCRIPT_DIR=$PWD TOOLS_DIR=$SCRIPT_DIR/tools CAKE_VERSION=0.24.0 CAKE_DLL=$TOOLS_DIR/Cake.$CAKE_VERSION/Cake.exe DOTNET_VERSION=$(cat "$SCRIPT_DIR/global.json" | grep -o '[0-9]\.[0-9]\.[0-9]') DOTNET_INSTRALL_URI=https://raw.githubusercontent.com/dotnet/cli/v$DOTNET_VERSION/scripts/obtain/dotnet-install.sh # Make sure the tools folder exist. if [ ! -d "$TOOLS_DIR" ]; then mkdir "$TOOLS_DIR" fi ########################################################################### # INSTALL .NET CORE CLI ########################################################################### echo "Installing .NET CLI..." if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then mkdir "$SCRIPT_DIR/.dotnet" fi curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" $DOTNET_INSTRALL_URI sudo bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" -c current --version $DOTNET_VERSION --install-dir .dotnet --no-path export PATH="$SCRIPT_DIR/.dotnet":$PATH export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 export DOTNET_CLI_TELEMETRY_OPTOUT=1 "$SCRIPT_DIR/.dotnet/dotnet" --info ########################################################################### # INSTALL CAKE ########################################################################### if [ ! -f "$CAKE_DLL" ]; then curl -Lsfo Cake.zip "https://www.nuget.org/api/v2/package/Cake/$CAKE_VERSION" && unzip -q Cake.zip -d "$TOOLS_DIR/Cake.$CAKE_VERSION" && rm -f Cake.zip if [ $? -ne 0 ]; then echo "An error occured while installing Cake." exit 1 fi fi # Make sure that Cake has been installed. if [ ! -f "$CAKE_DLL" ]; then echo "Could not find Cake.exe at '$CAKE_DLL'." exit 1 fi ########################################################################### # RUN BUILD SCRIPT ########################################################################### # Point net452 to Mono assemblies export FrameworkPathOverride=$(dirname $(which mono))/../lib/mono/4.5/ # Start Cake exec mono "$CAKE_DLL" "$@" ================================================ FILE: favicon.license.txt ================================================ The Nancy logo is copyright ©2011 by Andreas Håkansson and Steven Robbins. Please consult the usage guidelines in the Nancy.Portfolio repository (https://github.com/NancyFx/Nancy.Portfolio) for information on how it may be used. ================================================ FILE: global.json ================================================ { "sdk": { "version": "2.1.4" } } ================================================ FILE: how_to_build.txt ================================================ How to build Nancy ================== *NOTE* These instructions are *only* for building with Cake - if you just want to build Nancy manually you can do so just by loading the solution into Visual Studio 2017 and pressing build :-) Building Nancy -------------- 1. At the command prompt, navigate to the Nancy root folder (should contain build.cake) 2. To run the default build (which will compile, test and package Nancy) type the following command: * On Windows type: ./build.ps1 * On Linux/MacOS type: ./build.sh *NOTE* On Linux/MacOS you need to have Mono >= 4.4 installed because Nancy targets .NET 4.5.2 for the full-framework build After the build has completed, there will be a new folder in the root called "build". It contains the following folders: * binaries -> All the Nancy assembilies and their dependencies * packages -> Zip file containing the binaries (other configurations might be added in the future) * nuget -> NuGet packages generated from this build ================================================ FILE: license.txt ================================================ The MIT License Copyright (c) 2010 Andreas Hkansson, Steven Robbins and contributors 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. ================================================ FILE: samples/Nancy.Demo.Async/App.config ================================================  ================================================ FILE: samples/Nancy.Demo.Async/MainModule.cs ================================================ namespace Nancy.Demo.Async { using System; using System.Net.Http; using System.Threading.Tasks; public class MainModule : NancyModule { public MainModule() { Before += async (ctx, ct) => { this.AddToLog("Before Hook Delay\n"); await Task.Delay(5000); return null; }; After += async (ctx, ct) => { this.AddToLog("After Hook Delay\n"); await Task.Delay(5000); this.AddToLog("After Hook Complete\n"); ctx.Response = this.GetLog(); }; Get["/", true] = async (x, ct) => { this.AddToLog("Delay 1\n"); await Task.Delay(1000); this.AddToLog("Delay 2\n"); await Task.Delay(1000); this.AddToLog("Executing async http client\n"); var client = new HttpClient(); var res = await client.GetAsync("http://nancyfx.org"); var content = await res.Content.ReadAsStringAsync(); this.AddToLog("Response: " + content.Split('\n')[0] + "\n"); return (Response)this.GetLog(); }; } private void AddToLog(string logLine) { if (!this.Context.Items.ContainsKey("Log")) { this.Context.Items["Log"] = string.Empty; } this.Context.Items["Log"] = (string)this.Context.Items["Log"] + DateTime.Now.ToLongTimeString() + " : " + logLine; } private string GetLog() { if (!this.Context.Items.ContainsKey("Log")) { this.Context.Items["Log"] = string.Empty; } return (string)this.Context.Items["Log"]; } } } ================================================ FILE: samples/Nancy.Demo.Async/Nancy.Demo.Async.csproj ================================================  Debug AnyCPU {B4A5FF43-DE97-48CF-A759-1A868824791B} Exe Properties Nancy.Demo.Async Nancy.Demo.Async v4.5 512 AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 true bin\MonoDebug\ DEBUG;TRACE full AnyCPU prompt MinimumRecommendedRules.ruleset true bin\MonoRelease\ TRACE true pdbonly AnyCPU prompt MinimumRecommendedRules.ruleset true Properties\SharedAssemblyInfo.cs {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy ================================================ FILE: samples/Nancy.Demo.Async/Program.cs ================================================ namespace Nancy.Demo.Async { using System; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Nancy.Bootstrapper; class Program { static void Main(string[] args) { ShowHeader(); var engine = GetEngine(); var request = GetRequest(); var tcs = new TaskCompletionSource(); var completionTask = tcs.Task; Console.Write("Running: "); engine.HandleRequest(request, tcs.SetResult, tcs.SetException); while (!completionTask.IsCompleted && !completionTask.IsFaulted) { Console.Write("*"); Thread.Sleep(100); } string result = "Unknown"; if (completionTask.IsFaulted && completionTask.Exception != null) { result = completionTask.Exception.GetType().ToString(); } if (completionTask.IsCompleted && completionTask.Result != null) { result = GetBody(completionTask.Result); } Console.WriteLine("\nResult: \n\n{0}", result); Console.WriteLine("\nPress any key to close."); Console.ReadKey(); } private static INancyEngine GetEngine() { var bootstrapper = NancyBootstrapperLocator.Bootstrapper; bootstrapper.Initialise(); var engine = bootstrapper.GetEngine(); return engine; } private static void ShowHeader() { Console.WriteLine("Async Demo"); Console.WriteLine(); Console.WriteLine("A long running async request will be executed and until it is complete"); Console.WriteLine("a series of '*' characters should appear every 100 milliseconds. If this was"); Console.WriteLine("executed syncronously then the main thread would block and no characters"); Console.WriteLine("would appear."); Console.WriteLine(); } private static string GetBody(NancyContext result) { string output; using (var memoryStream = new MemoryStream()) { result.Response.Contents.Invoke(memoryStream); output = Encoding.UTF8.GetString(memoryStream.GetBuffer()); } return output; } private static Request GetRequest(string path = "/") { return new Request("GET", path, "http"); } } } ================================================ FILE: samples/Nancy.Demo.Authentication/AnotherVerySecureModule.cs ================================================ namespace Nancy.Demo.Authentication { using System.Security.Claims; using Nancy.Demo.Authentication.Models; using Nancy.Security; /// /// A module that only people with SuperSecure clearance are allowed to access /// public class AnotherVerySecureModule : NancyModule { public AnotherVerySecureModule() : base("/superSecure") { this.RequiresClaims(c => c.Type == ClaimTypes.Role && c.Value == "SuperSecure"); Get("/", args => { var model = new UserModel(this.Context.CurrentUser.Identity.Name); return View["superSecure.cshtml", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication/AuthenticationBootstrapper.cs ================================================ namespace Nancy.Demo.Authentication { using System; using System.Collections.Generic; using System.Security.Claims; using Nancy.Bootstrapper; using Nancy.Responses; using Nancy.TinyIoc; public class AuthenticationBootstrapper : DefaultNancyBootstrapper { protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); // In reality you would use a pre-built authentication/claims provider pipelines.BeforeRequest += ctx => { // World's-worse-authentication (TM) // Pull the username out of the querystring if it exists // and build claims from it var username = ctx.Request.Query.username; if (username.HasValue) { ctx.CurrentUser = new ClaimsPrincipal(new ClaimsIdentity(BuildClaims(username), "querystring")); } return null; }; pipelines.AfterRequest += ctx => { // If status code comes back as Unauthorized then // forward the user to the login page if (ctx.Response.StatusCode == HttpStatusCode.Unauthorized) { ctx.Response = new RedirectResponse("/login?returnUrl=" + Uri.EscapeDataString(ctx.Request.Path)); } }; } /// /// Build claims based on username /// /// Current username /// IEnumerable of claims private static IEnumerable BuildClaims(string userName) { var claims = new List(); // Only bob can have access to SuperSecure if (String.Equals(userName, "bob", StringComparison.OrdinalIgnoreCase)) { claims.Add(new Claim(ClaimTypes.Role, "SuperSecure")); } return claims; } } } ================================================ FILE: samples/Nancy.Demo.Authentication/MainModule.cs ================================================ namespace Nancy.Demo.Authentication { public class MainModule : NancyModule { public MainModule() { Get("/", args => { return View["Index.cshtml"]; }); Get("/login", args => { return View["Login.cshtml", this.Request.Query.returnUrl]; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication/Models/UserModel.cs ================================================ namespace Nancy.Demo.Authentication.Models { public class UserModel { public string Username { get; set; } public UserModel(string username) { this.Username = username; } } } ================================================ FILE: samples/Nancy.Demo.Authentication/Nancy.Demo.Authentication.csproj ================================================  Debug AnyCPU 2.0 {5F2DD52D-471B-4946-8F7B-F050C88EFC52} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Authentication Nancy.Demo.Authentication v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Authentication.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Authentication.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {2C6F51DF-015C-4DB6-B44C-0E5E4F25E2A9} Nancy.ViewEngines.Razor {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 1712 / False False False ================================================ FILE: samples/Nancy.Demo.Authentication/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication/SecureModule.cs ================================================ namespace Nancy.Demo.Authentication { using Nancy.Demo.Authentication.Models; using Nancy.Security; public class SecureModule : NancyModule { public SecureModule() : base("/secure") { this.RequiresAuthentication(); Get("/", args => { var model = new UserModel(this.Context.CurrentUser.Identity.Name); return View["secure.cshtml", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication/Views/Index.cshtml ================================================  Index

Nancy Authentication Demo

Secure Pages Super Secure Pages More Super Secure Pages ================================================ FILE: samples/Nancy.Demo.Authentication/Views/Login.cshtml ================================================  Login

Super Secure Login Page 4000

Home ================================================ FILE: samples/Nancy.Demo.Authentication/Views/secure.cshtml ================================================  Index

Secure Page

Hello @Model.Username Home ================================================ FILE: samples/Nancy.Demo.Authentication/Views/superSecure.cshtml ================================================  Index

Super Secure Page

Hello @Model.Username Home ================================================ FILE: samples/Nancy.Demo.Authentication/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/AuthenticationBootstrapper.cs ================================================ namespace Nancy.Demo.Authentication.Basic { using Nancy.Authentication.Basic; using Nancy.Bootstrapper; using Nancy.TinyIoc; public class AuthenticationBootstrapper : DefaultNancyBootstrapper { protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration( container.Resolve(), "MyRealm")); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/MainModule.cs ================================================ namespace Nancy.Demo.Authentication.Basic { public class MainModule : NancyModule { public MainModule() { Get("/", args => "Enter"); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/Nancy.Demo.Authentication.Basic.csproj ================================================  Debug AnyCPU 2.0 {911196F4-B88F-4940-B1FD-D30F5290D06D} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Authentication.Basic Nancy.Demo.Authentication.Basic v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Authentication.Basic.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Authentication.Basic.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false false 4 false Web.config Web.config {BD72B98D-C81A-4013-B606-94B4BA2273E5} Nancy.Authentication.Basic {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 12615 / False False False ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/SecureModule.cs ================================================ namespace Nancy.Demo.Authentication.Basic { using Nancy.Security; public class SecureModule : NancyModule { public SecureModule() : base("/secure") { this.RequiresAuthentication(); Get("/", args => "Hello " + this.Context.CurrentUser.Identity.Name); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/UserValidator.cs ================================================ namespace Nancy.Demo.Authentication.Basic { using System.Security.Claims; using System.Security.Principal; using Nancy.Authentication.Basic; public class UserValidator : IUserValidator { public ClaimsPrincipal Validate(string username, string password) { if (username == "demo" && password == "demo") { return new ClaimsPrincipal(new GenericIdentity(username)); } // Not recognised => anonymous. return null; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Basic/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/FormsAuthBootstrapper.cs ================================================ namespace Nancy.Demo.Authentication.Forms { using Nancy.Authentication.Forms; using Nancy.Bootstrapper; using Nancy.TinyIoc; public class FormsAuthBootstrapper : DefaultNancyBootstrapper { protected override void ConfigureApplicationContainer(TinyIoCContainer container) { // We don't call "base" here to prevent auto-discovery of // types/dependencies } protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context) { base.ConfigureRequestContainer(container, context); // Here we register our user mapper as a per-request singleton. // As this is now per-request we could inject a request scoped // database "context" or other request scoped services. container.Register(); } protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context) { // At request startup we modify the request pipelines to // include forms authentication - passing in our now request // scoped user name mapper. // // The pipelines passed in here are specific to this request, // so we can add/remove/update items in them as we please. var formsAuthConfiguration = new FormsAuthenticationConfiguration() { RedirectUrl = "~/login", UserMapper = requestContainer.Resolve(), }; FormsAuthentication.Enable(pipelines, formsAuthConfiguration); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/MainModule.cs ================================================ namespace Nancy.Demo.Authentication.Forms { using System; using System.Dynamic; using Nancy.Authentication.Forms; using Nancy.Extensions; public class MainModule : NancyModule { public MainModule() { Get("/", args => { return View["index"]; }); Get("/login", args => { dynamic model = new ExpandoObject(); model.Errored = this.Request.Query.error.HasValue; return View["login", model]; }); Post("/login", args => { var userGuid = UserDatabase.ValidateUser((string)this.Request.Form.Username, (string)this.Request.Form.Password); if (userGuid == null) { return this.Context.GetRedirect("~/login?error=true&username=" + (string)this.Request.Form.Username); } DateTime? expiry = null; if (this.Request.Form.RememberMe.HasValue) { expiry = DateTime.Now.AddDays(7); } return this.LoginAndRedirect(userGuid.Value, expiry); }); Get("/logout", args => { return this.LogoutAndRedirect("~/"); }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Models/UserModel.cs ================================================ namespace Nancy.Demo.Authentication.Forms.Models { public class UserModel { public string Username { get; private set; } public UserModel(string username) { Username = username; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Nancy.Demo.Authentication.Forms.csproj ================================================  Debug AnyCPU 2.0 {98940A30-1B48-4F71-A6BA-85F0AAF31A2F} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Authentication.Forms Nancy.Demo.Authentication.Forms v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Authentication.Forms.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Authentication.Forms.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {E8B18958-7C8A-4FBA-AF00-3041C34A20CE} Nancy.Authentication.Forms {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {2C6F51DF-015C-4DB6-B44C-0E5E4F25E2A9} Nancy.ViewEngines.Razor {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 2146 /forms False False False ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/PartlySecureModule.cs ================================================ namespace Nancy.Demo.Authentication.Forms { using Nancy.Demo.Authentication.Forms.Models; using Nancy.Security; public class PartlySecureModule : NancyModule { public PartlySecureModule() : base("/partlysecure") { Get("/", args => "No auth needed! Enter the secure bit!"); Get("/secured", args => { this.RequiresAuthentication(); var model = new UserModel(this.Context.CurrentUser.Identity.Name); return View["secure.cshtml", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/SecureModule.cs ================================================ namespace Nancy.Demo.Authentication.Forms { using Nancy.Demo.Authentication.Forms.Models; using Nancy.Security; public class SecureModule : NancyModule { public SecureModule() : base("/secure") { this.RequiresAuthentication(); Get("/", args => { var model = new UserModel(this.Context.CurrentUser.Identity.Name); return View["secure.cshtml", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/UserDatabase.cs ================================================ namespace Nancy.Demo.Authentication.Forms { using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Security.Principal; using Nancy.Authentication.Forms; public class UserDatabase : IUserMapper { private static List> users = new List>(); static UserDatabase() { users.Add(new Tuple("admin", "password", new Guid("55E1E49E-B7E8-4EEA-8459-7A906AC4D4C0"))); users.Add(new Tuple("user", "password", new Guid("56E1E49E-B7E8-4EEA-8459-7A906AC4D4C0"))); } public ClaimsPrincipal GetUserFromIdentifier(Guid identifier, NancyContext context) { var userRecord = users.FirstOrDefault(u => u.Item3 == identifier); return userRecord == null ? null : new ClaimsPrincipal(new GenericIdentity(userRecord.Item1)); } public static Guid? ValidateUser(string username, string password) { var userRecord = users.FirstOrDefault(u => u.Item1 == username && u.Item2 == password); if (userRecord == null) { return null; } return userRecord.Item3; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Views/index.cshtml ================================================  Forms Authentication Demo Enter the "Secure Zone"!
Enter the "Partly Secure Zone"!
================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Views/login.cshtml ================================================  Login
Username
Password
Remember Me
@if (Model.Errored) {
Invalid Username or Password
} ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Views/secure.cshtml ================================================  Secure Page! Welcome to the secure area @Model.Username !

Logout ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Forms/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Forms.TestingDemo/LoginFixture.cs ================================================ namespace Nancy.Demo.Authentication.Forms.TestingDemo { using System; using System.Threading.Tasks; using Nancy.Testing; using Xunit; public class LoginFixture { private readonly Browser browser; public LoginFixture() { var bootstrapper = new TestBootstrapper(); this.browser = new Browser(bootstrapper); } [Fact] public async Task Should_redirect_to_login_with_error_querystring_if_username_or_password_incorrect() { // Given, When var response = await browser.Post("/login/", (with) => { with.HttpRequest(); with.FormValue("Username", "username"); with.FormValue("Password", "wrongpassword"); }); response.ShouldHaveRedirectedTo("/login?error=true&username=username"); } [Fact] public async Task Should_display_error_message_when_error_passed() { // Given, When var response = await browser.Get("/login", (with) => { with.HttpRequest(); with.Query("error", "true"); }); response.Body["#errorBox"] .ShouldExistOnce() .And.ShouldBeOfClass("floatingError") .And.ShouldContain("invalid", StringComparison.OrdinalIgnoreCase); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms.TestingDemo/Nancy.Demo.Authentication.Forms.TestingDemo.csproj ================================================  Debug AnyCPU 8.0.30703 2.0 {948A8EF6-D50C-45EA-9AFD-7A4723ADAB0B} Library Properties Nancy.Demo.Authentication.Forms.TestingDemo Nancy.Demo.Authentication.Forms.TestingDemo v4.5 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset false true bin\MonoDebug\ DEBUG;TRACE full AnyCPU bin\Debug\Nancy.Demo.Authentication.Forms.TestingDemo.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false false bin\MonoRelease\ TRACE true pdbonly AnyCPU bin\Release\Nancy.Demo.Authentication.Forms.TestingDemo.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll True ..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll True ..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll True ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll True ShouldExtensions.cs Properties\SharedAssemblyInfo.cs {d79203c0-b672-4751-9c95-c3ab7d3fefbe} Nancy.Testing {98940A30-1B48-4F71-A6BA-85F0AAF31A2F} Nancy.Demo.Authentication.Forms {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {2C6F51DF-015C-4DB6-B44C-0E5E4F25E2A9} Nancy.ViewEngines.Razor {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy ================================================ FILE: samples/Nancy.Demo.Authentication.Forms.TestingDemo/TestBootstrapper.cs ================================================ namespace Nancy.Demo.Authentication.Forms.TestingDemo { public class TestBootstrapper : FormsAuthBootstrapper { protected override IRootPathProvider RootPathProvider { get { return new TestRootPathProvider(); } } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms.TestingDemo/TestRootPathProvider.cs ================================================ namespace Nancy.Demo.Authentication.Forms.TestingDemo { using System; using System.IO; using Nancy.Testing; public class TestRootPathProvider : IRootPathProvider { public string GetRootPath() { var assemblyFilePath = new Uri(typeof(FormsAuthBootstrapper).Assembly.CodeBase).LocalPath; var assemblyPath = Path.GetDirectoryName(assemblyFilePath); var rootPath = PathHelper.GetParent(assemblyPath, 3); rootPath = Path.Combine(rootPath, @"Nancy.Demo.Authentication.Forms"); return rootPath; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Forms.TestingDemo/packages.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/AuthModule.cs ================================================ namespace Nancy.Demo.Authentication.Stateless { public class AuthModule : NancyModule { public AuthModule() : base("/auth/") { //the Post["/login"] method is used mainly to fetch the api key for subsequent calls Post("/", args => { var apiKey = UserDatabase.ValidateUser( (string) this.Request.Form.Username, (string) this.Request.Form.Password); return string.IsNullOrEmpty(apiKey) ? new Response {StatusCode = HttpStatusCode.Unauthorized} : this.Response.AsJson(new {ApiKey = apiKey}); }); //do something to destroy the api key, maybe? Delete("/", args => { var apiKey = (string) this.Request.Form.ApiKey; UserDatabase.RemoveApiKey(apiKey); return new Response {StatusCode = HttpStatusCode.OK}; }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/Models/UserModel.cs ================================================ namespace Nancy.Demo.Authentication.Stateless.Models { public class UserModel { public string Username { get; private set; } public UserModel(string username) { Username = username; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/Nancy.Demo.Authentication.Stateless.csproj ================================================  true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Authentication.Stateless.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Authentication.Stateless.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false Debug AnyCPU 2.0 {BAE74CD5-57C2-40E3-8F7A-EDE5721C2ACC} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Nancy.Demo.Authentication.Stateless Nancy.Demo.Authentication.Stateless v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 false pdbonly true bin\ TRACE prompt 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {211560C3-FDDF-46D6-AB0C-F3BC04B173B5} Nancy.Authentication.Stateless {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False False 55581 /restApi False False False ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/RootModule.cs ================================================ namespace Nancy.Demo.Authentication.Stateless { public class RootModule : NancyModule { public RootModule() { Get("/", args => this.Response.AsText("This is a REST API. It is in another VS project to " + "demonstrate how a common REST API might behave when " + "accessing it from another website or application. To " + "see how a website can access this API, run the " + "Nancy.Demo.Authentication.Stateless.Website project " + "(in the same Nancy solution).")); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/SecureModule.cs ================================================ namespace Nancy.Demo.Authentication.Stateless { using System; using Nancy.Demo.Authentication.Stateless.Models; using Nancy.Security; public class SecureModule : NancyModule { //by this time, the api key should have already been pulled out of our querystring //and, using the api key, an identity assigned to our NancyContext public SecureModule() { this.RequiresAuthentication(); Get("secure", args => { //Context.CurrentUser was set by StatelessAuthentication earlier in the pipeline var identity = this.Context.CurrentUser; //return the secure information in a json response var userModel = new UserModel(identity.Identity.Name); return this.Response.AsJson(new { SecureContent = "here's some secure content that you can only see if you provide a correct apiKey", User = userModel }); }); Post("secure/create_user", args => { Tuple user = UserDatabase.CreateUser(this.Context.Request.Form["username"], this.Context.Request.Form["password"]); return this.Response.AsJson(new { username = user.Item1 }); }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/StatelessAuthBootstrapper.cs ================================================ namespace Nancy.Demo.Authentication.Stateless { using Nancy.Authentication.Stateless; using Nancy.Bootstrapper; using Nancy.TinyIoc; public class StatelessAuthBootstrapper : DefaultNancyBootstrapper { protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context) { // At request startup we modify the request pipelines to // include stateless authentication // // Configuring stateless authentication is simple. Just use the // NancyContext to get the apiKey. Then, use the apiKey to get // your user's identity. var configuration = new StatelessAuthenticationConfiguration(nancyContext => { //for now, we will pull the apiKey from the querystring, //but you can pull it from any part of the NancyContext var apiKey = (string) nancyContext.Request.Query.ApiKey.Value; //get the user identity however you choose to (for now, using a static class/method) return UserDatabase.GetUserFromApiKey(apiKey); }); AllowAccessToConsumingSite(pipelines); StatelessAuthentication.Enable(pipelines, configuration); } static void AllowAccessToConsumingSite(IPipelines pipelines) { pipelines.AfterRequest.AddItemToEndOfPipeline(x => { x.Response.Headers.Add("Access-Control-Allow-Origin", "*"); x.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT,OPTIONS"); }); } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/UserDatabase.cs ================================================ namespace Nancy.Demo.Authentication.Stateless { using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Security.Principal; public class UserDatabase { static readonly List> ActiveApiKeys = new List>(); private static readonly List> Users = new List>(); static UserDatabase() { Users.Add(new Tuple("admin", "password")); Users.Add(new Tuple("user", "password")); } public static ClaimsPrincipal GetUserFromApiKey(string apiKey) { var activeKey = ActiveApiKeys.FirstOrDefault(x => x.Item2 == apiKey); if(activeKey==null) { return null; } var userRecord = Users.First(u => u.Item1 == activeKey.Item1); return new ClaimsPrincipal(new GenericIdentity(userRecord.Item1, "stateless")); } public static string ValidateUser(string username, string password) { //try to get a user from the "database" that matches the given username and password var userRecord = Users.FirstOrDefault(u => u.Item1 == username && u.Item2 == password); if(userRecord==null) { return null; } //now that the user is validated, create an api key that can be used for subsequent requests var apiKey = Guid.NewGuid().ToString(); ActiveApiKeys.Add(new Tuple(username, apiKey)); return apiKey; } public static void RemoveApiKey(string apiKey) { var apiKeyToRemove = ActiveApiKeys.First(x => x.Item2 == apiKey); ActiveApiKeys.Remove(apiKeyToRemove); } public static Tuple CreateUser(string username, string password) { var user = new Tuple(username, password); Users.Add(user); return user; } } } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Nancy.Demo.Authentication.Stateless.Website.csproj ================================================  true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Authentication.Stateless.Website.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Authentication.Stateless.Website.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt MinimumRecommendedRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false Debug AnyCPU 2.0 {B5E3586D-81DE-49C3-83BC-062684795127} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Nancy.Demo.Authentication.Stateless.Website Nancy.Demo.Authentication.Stateless.Website v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 false pdbonly true bin\ TRACE prompt 4 false Always Always Always Web.config Web.config Properties\SharedAssemblyInfo.cs 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False False 51101 /statelessWebsite False False False ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Scripts/api.js ================================================ var api = { auth: "http://localhost:55581/restApi/auth", secure: "http://localhost:55581/restApi/secure", create_user: "http://localhost:55581/restApi/secure/create_user" }; ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Scripts/apiToken.js ================================================ var apiKeyKey = "sample_apiKey"; var usernameKey = "sample_username"; var ApiToken = { Set: function (username, apiKey, rememberMe) { var days = rememberMe ? 10 : 0; createCookie(apiKeyKey, apiKey, days); createCookie(usernameKey, username, days); }, Get: function () { var key = readCookie(apiKeyKey); var username = readCookie(usernameKey); var token = { Key: key, Username: username, IsValid: key != null }; return token; }, Delete: function () { eraseCookie(apiKeyKey); eraseCookie(usernameKey); } }; function createCookie(name, value, days) { if (days) { var date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = "; expires=" + date.toGMTString(); } else var expires = ""; document.cookie = name + "=" + value + expires + "; path=/"; console.log(document.cookie); } function readCookie(name) { var cookieValue = null; var nameEQ = name + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) { var found = c.substring(nameEQ.length, c.length); cookieValue = found; } } return cookieValue; } function eraseCookie(name) { createCookie(name, "", -1); } ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/index.html ================================================  Stateless Authentication Demo

Non-Secure Zone

Enter the "Secure Zone"!

================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/login.html ================================================  Login
Username
Password
Remember Me
================================================ FILE: samples/Nancy.Demo.Authentication.Stateless.Website/secure.html ================================================  Stateless Authentication Demo
<< Home

Secure Zone

Secure Content:
Username
Password
================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/ApplicationDependencyClass.cs ================================================ namespace Nancy.AspNetBootstrapperDemo { using System; using Nancy.Demo.Bootstrapping.Aspnet; /// /// A module dependency that will have an application lifetime scope. /// public class ApplicationDependencyClass : IApplicationDependency { private readonly DateTime currentDateTime; /// /// Initializes a new instance of the RequestDependencyClass class. /// public ApplicationDependencyClass() { this.currentDateTime = DateTime.Now; } public string GetContent() { return "This is an application level dependency, constructed on: " + this.currentDateTime.ToLongTimeString(); } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Bootstrapper.cs ================================================ namespace Nancy.AspNetBootstrapperDemo { using Nancy.Demo.Bootstrapping.Aspnet; using Nancy.Hosting.Aspnet; using Nancy.TinyIoc; public class Bootstrapper : DefaultNancyAspNetBootstrapper { protected override void ConfigureApplicationContainer(TinyIoCContainer container) { // Register our app dependency as a normal singleton container.Register().AsSingleton(); // Register our per-request dependency as a per-request singleton container.Register().AsPerRequestSingleton(); } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/DependencyModule.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet { using Nancy.Demo.Bootstrapping.Aspnet.Models; public class DependencyModule : NancyModule { private readonly IApplicationDependency applicationDependency; private readonly IRequestDependency requestDependency; public DependencyModule(IApplicationDependency applicationDependency, IRequestDependency requestDependency) { this.applicationDependency = applicationDependency; this.requestDependency = requestDependency; Get("/", args => { var model = new RatPackWithDependencyText { FirstName = "Bob", ApplicationDependencyText = this.applicationDependency.GetContent(), RequestDependencyText = this.requestDependency.GetContent() }; return View["razor-dependency", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/IApplicationDependency.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet { public interface IApplicationDependency { string GetContent(); } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/IRequestDependency.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet { public interface IRequestDependency { string GetContent(); } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Models/RatPack.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet.Models { public class RatPack { public string FirstName { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Models/RatPackWithDependencyText.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet.Models { public class RatPackWithDependencyText : RatPack { public string ApplicationDependencyText { get; set; } public string RequestDependencyText { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Nancy.Demo.Bootstrapping.Aspnet.csproj ================================================  Debug AnyCPU 2.0 {EF660223-4DFD-4E36-BF36-9DD6AFB3F837} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Bootstrapping.Aspnet Nancy.Demo.Bootstrapping.Aspnet v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Bootstrapping.Aspnet.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Bootstrapping.Aspnet.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {2C6F51DF-015C-4DB6-B44C-0E5E4F25E2A9} Nancy.ViewEngines.Razor {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 10220 / False False False ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/RequestDependencyClass.cs ================================================ namespace Nancy.Demo.Bootstrapping.Aspnet { using System; /// /// A module dependency that will have a per-request lifetime scope. /// public class RequestDependencyClass : IRequestDependency { private readonly DateTime currentDateTime; /// /// Initializes a new instance of the RequestDependencyClass class. /// public RequestDependencyClass() { this.currentDateTime = DateTime.Now; } public string GetContent() { return "This is a per-request dependency, constructed on: " + this.currentDateTime.ToLongTimeString(); } } } ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Views/razor-dependency.cshtml ================================================  Razor View Engine Demo

Hello @Model.FirstName

This is a Razor view..

@Model.ApplicationDependencyText

@Model.RequestDependencyText

================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Bootstrapper.Aspnet/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Caching/CachedResponse.cs ================================================ namespace Nancy.Demo.Caching { using System; using System.IO; using System.Text; using System.Threading.Tasks; using Nancy; /// /// Wraps a regular response in a cached response /// The cached response invokes the old response and stores it as a string. /// Obviously this only works for ASCII text based responses, so don't use this /// in a real application :-) /// public class CachedResponse : Response { private readonly Response response; public CachedResponse(Response response) { this.response = response; this.ContentType = response.ContentType; this.Headers = response.Headers; this.StatusCode = response.StatusCode; this.Contents = this.GetContents(); } public override Task PreExecute(NancyContext context) { return this.response.PreExecute(context); } private Action GetContents() { return stream => { using (var memoryStream = new MemoryStream()) { this.response.Contents.Invoke(memoryStream); var contents = Encoding.ASCII.GetString(memoryStream.GetBuffer()); var writer = new StreamWriter(stream) { AutoFlush = true }; writer.Write(contents); } }; } } } ================================================ FILE: samples/Nancy.Demo.Caching/CachingBootstrapper.cs ================================================ namespace Nancy.Demo.Caching { using System; using System.Collections.Generic; using Nancy.Bootstrapper; using Nancy.Demo.Caching.CachingExtensions; using Nancy.TinyIoc; public class CachingBootstrapper : DefaultNancyBootstrapper { private const int CACHE_SECONDS = 30; private readonly Dictionary> cachedResponses = new Dictionary>(); protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); pipelines.BeforeRequest += CheckCache; pipelines.AfterRequest += SetCache; } /// /// Check to see if we have a cache entry - if we do, see if it has expired or not, /// if it hasn't then return it, otherwise return null; /// /// Current context /// Request or null public Response CheckCache(NancyContext context) { Tuple cacheEntry; if (this.cachedResponses.TryGetValue(context.Request.Path, out cacheEntry)) { if (cacheEntry.Item1.AddSeconds(cacheEntry.Item3) > DateTime.Now) { return cacheEntry.Item2; } } return null; } /// /// Adds the current response to the cache if required /// Only stores by Path and stores the response in a dictionary. /// Do not use this as an actual cache :-) /// /// Current context public void SetCache(NancyContext context) { if (context.Response.StatusCode != HttpStatusCode.OK) { return; } object cacheSecondsObject; if (!context.Items.TryGetValue(ContextExtensions.OUTPUT_CACHE_TIME_KEY, out cacheSecondsObject)) { return; } int cacheSeconds; if (!int.TryParse(cacheSecondsObject.ToString(), out cacheSeconds)) { return; } var cachedResponse = new CachedResponse(context.Response); this.cachedResponses[context.Request.Path] = new Tuple(DateTime.Now, cachedResponse, cacheSeconds); context.Response = cachedResponse; } } } ================================================ FILE: samples/Nancy.Demo.Caching/CachingExtensions/ContextExtensions.cs ================================================ namespace Nancy.Demo.Caching.CachingExtensions { public static class ContextExtensions { public const string OUTPUT_CACHE_TIME_KEY = "OUTPUT_CACHE_TIME"; /// /// Enable output caching for this route /// /// Current context /// Seconds to cache for public static void EnableOutputCache(this NancyContext context, int seconds) { context.Items[OUTPUT_CACHE_TIME_KEY] = seconds; } /// /// Disable the output cache for this route /// /// Current context public static void DisableOutputCache(this NancyContext context) { context.Items.Remove(OUTPUT_CACHE_TIME_KEY); } } } ================================================ FILE: samples/Nancy.Demo.Caching/MainModule.cs ================================================ namespace Nancy.Demo.Caching { using System; using Nancy.Demo.Caching.CachingExtensions; public class MainModule : NancyModule { public MainModule() { Get("/", args => { return View["Index.cshtml", DateTime.Now.ToString()]; }); Get("/cached", args => { this.Context.EnableOutputCache(30); return View["Payload.cshtml", DateTime.Now.ToString()]; }); Get("/uncached", args => { this.Context.DisableOutputCache(); return View["Payload.cshtml", DateTime.Now.ToString()]; }); } } } ================================================ FILE: samples/Nancy.Demo.Caching/Nancy.Demo.Caching.csproj ================================================  Debug AnyCPU 2.0 {28F9EA8B-90F7-4974-BB40-0B7FA9309D02} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Caching Nancy.Demo.Caching v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Caching.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Caching.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {2C6F51DF-015C-4DB6-B44C-0E5E4F25E2A9} Nancy.ViewEngines.Razor {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 3486 / False False False ================================================ FILE: samples/Nancy.Demo.Caching/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Caching/Views/Index.cshtml ================================================  Index

Nancy Caching Demo

Cached

Uncached

================================================ FILE: samples/Nancy.Demo.Caching/Views/Payload.cshtml ================================================  Index

Nancy Caching Demo

This page was created on: @Model

This may or may not be cached :-)

Home ================================================ FILE: samples/Nancy.Demo.Caching/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Caching/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Caching/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/ConstraintRoutingModule.cs ================================================ namespace Nancy.Demo.ModelBinding { public class ConstraintRoutingModule : NancyModule { public ConstraintRoutingModule() { Get("/", args => View["Index"]); Get("/intConstraint/{value:int}", args => "Value " + args.value + " is an integer."); Get("/decimalConstraint/{value:decimal}", args => "Value " + args.value + " is a decimal."); Get("/guidConstraint/{value:guid}", args => "Value " + args.value + " is a guid."); Get("/boolConstraint/{value:bool}", args => "Value " + args.value + " is a boolean."); Get("/alphaConstraint/{value:alpha}", args => "Value " + args.value + " is only letters."); Get("/datetimeConstraint/{value:datetime}", args => "Value " + args.value + " is a date time."); Get("/customDatetimeConstraint/{value:datetime(yyyy-MM-dd)}", args => "Value " + args.value + " is a date time with format 'yyyy-MM-dd'."); Get("/minConstraint/{value:min(4)}", args => "Value " + args.value + " is an integer greater than 4."); Get("/maxConstraint/{value:max(6)}", args => "Value " + args.value + " is an integer less than 6."); Get("/rangeConstraint/{value:range(10, 20)}", args => "Value " + args.value + " is an integer between 10 and 20."); Get("/minlengthConstraint/{value:minlength(4)}", args => "Value " + args.value + " is a string with length greater than 4."); Get("/maxlengthConstraint/{value:maxlength(10)}", args => "Value " + args.value + " is a string with length less than 10."); Get("/lengthConstraint/{value:length(1, 20)}", args => "Value " + args.value + " is a string with length between 1 and 20."); Get("/versionConstraint/{value:version}", args => "Value " + args.value + " is a version number."); Get("/emailConstraint/{value:email}", args => "Value " + args.value + " is an e-mail address (according to @jchannon)."); } } } ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/EmailRouteSegmentConstraint.cs ================================================ namespace Nancy.Demo.ModelBinding { using Nancy.Routing.Constraints; public class EmailRouteSegmentConstraint : RouteSegmentConstraintBase { public override string Name { get { return "email"; } } protected override bool TryMatch(string constraint, string segment, out string matchedValue) { // Using @jchannon logic for validating e-mail address if (segment.Contains("@") && segment.Contains(".")) { matchedValue = segment; return true; } matchedValue = null; return false; } } } ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/Nancy.Demo.ConstraintRouting.csproj ================================================  Debug AnyCPU 2.0 {972C2D45-49B6-4109-9A3A-0C8BC9225B95} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.ModelBinding Nancy.Demo.ModelBinding v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.ModelBinding.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.ModelBinding.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15b7f794-0bb2-4b66-ad78-4a951f1209b2} Nancy.Hosting.Aspnet {34576216-0dca-4b0f-a0dc-9075e75a676f} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 56281 / False False False ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/Views/Index.html ================================================ 

Constraint Routing

Constraint
Constraint Description Expression Example
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha} Success Failure
bool Matches a Boolean value. {x:bool} Success Failure
datetime Matches a DateTime value. {x:datetime} Success Failure
datetime Matches a DateTime value with a custom format. {x:datetime(yyyy-MM-dd)} Success Failure
decimal Matches a decimal value. {x:decimal} Success Failure
guid Matches a GUID value. {x:guid} Success Failure
int Matches a integer value. {x:int} Success Failure
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
Success Failure
max Matches an integer with a maximum value. {x:max(6)} Success Failure
maxlength Matches a string with a maximum length. {x:maxlength(10)} Success Failure
min Matches an integer with a minimum value. {x:min(4)} Success Failure
minlength Matches a string with a minimum length. {x:minlength(4)} Success Failure
range Matches an integer within a range of values. {x:range(10,20)} Success Failure
version Matches a version number. {x:version} Success Failure
email Matches an e-mail address (according to @jchannon). {x:email} Success Failure
================================================ FILE: samples/Nancy.Demo.ConstraintRouting/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.ConstraintRouting/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.CustomModule/DemoBootstrapper.cs ================================================ namespace Nancy.Demo.CustomModule { using Nancy.Configuration; using Nancy.Diagnostics; public class DemoBootstrapper : DefaultNancyBootstrapper { public override void Configure(INancyEnvironment environment) { environment.Diagnostics( enabled: true, password: "password"); } } } ================================================ FILE: samples/Nancy.Demo.CustomModule/MainModule.cs ================================================ namespace Nancy.Demo.CustomModule { public class MainModule : UglifiedNancyModule { [NancyRoute("GET", "/")] public dynamic Root(dynamic parameters) { return View["Index", new { Name = "Jimbo!" }]; } public bool FilteredFilter(NancyContext context) { return false; } [NancyRoute("GET", "/filtered")] public dynamic Filtered(dynamic parameters) { return "This is filtered"; } } } ================================================ FILE: samples/Nancy.Demo.CustomModule/Nancy.Demo.CustomModule.csproj ================================================  Debug AnyCPU 2.0 {F56F3983-0C34-4AEC-B418-A8AFFA63F1C4} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Nancy.Demo.CustomModule Nancy.Demo.CustomModule v4.5 true true full false bin\ DEBUG;TRACE prompt 4 false pdbonly true bin\ TRACE prompt 4 false Web.config Web.config {15b7f794-0bb2-4b66-ad78-4a951f1209b2} Nancy.Hosting.Aspnet {34576216-0dca-4b0f-a0dc-9075e75a676f} Nancy SharedAssemblyInfo.cs 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true bin\ DEBUG;TRACE full AnyCPU prompt MinimumRecommendedRules.ruleset false bin\ TRACE true pdbonly AnyCPU prompt MinimumRecommendedRules.ruleset false False True 56673 / http://localhost:53007/ False False False ================================================ FILE: samples/Nancy.Demo.CustomModule/NancyRouteAttribute.cs ================================================ namespace Nancy.Demo.CustomModule { using System; public class NancyRouteAttribute : Attribute { /// /// The method for the route /// public string Method { get; set; } /// /// The path for the route /// public string Path { get; set; } public NancyRouteAttribute(string method, string path) { this.Method = method; this.Path = path; } } } ================================================ FILE: samples/Nancy.Demo.CustomModule/UglifiedNancyModule.cs ================================================ namespace Nancy.Demo.CustomModule { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Validation; using Nancy.ViewEngines; /// /// A custom INancyModule implementation that uses /// attributes on methods (eugh!) to define routes. /// Nobody in their right mind would write a web framework /// that uses attributes for routing ;-) /// public abstract class UglifiedNancyModule : INancyModule { public AfterPipeline After { get; set; } public BeforePipeline Before { get; set; } public ErrorPipeline OnError { get; set; } public NancyContext Context { get; set; } public IResponseFormatter Response { get; set; } public IModelBinderLocator ModelBinderLocator { get; set; } public ModelValidationResult ModelValidationResult { get; set; } public IModelValidatorLocator ValidatorLocator { get; set; } public Request Request { get; set; } public IViewFactory ViewFactory { get; set; } public string ModulePath { get; private set; } public ViewRenderer View { get { return new ViewRenderer(this); } } public Negotiator Negotiate { get { return new Negotiator(this.Context); } } public UglifiedNancyModule() : this(string.Empty) { } public IEnumerable Routes { get { return this.GetRoutes(); } } public dynamic Text { get; set; } private UglifiedNancyModule(string modulePath) { this.After = new AfterPipeline(); this.Before = new BeforePipeline(); this.OnError = new ErrorPipeline(); this.ModulePath = modulePath; } private IEnumerable GetRoutes() { // Run through all the methods on the class looking // for our attribute. If we were to do this for a real // app we'd be checking parameters and return types etc // but for simplicity we won't bother here. var routes = new List(); var type = this.GetType(); var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (var method in methods) { var attribute = method.GetCustomAttributes(typeof(NancyRouteAttribute), false).FirstOrDefault() as NancyRouteAttribute; if (attribute == null) { continue; } var routeDelegate = WrapFunc((Func)Delegate.CreateDelegate(typeof(Func), this, method.Name)); var filter = this.GetFilter(method.Name); var fullPath = string.Concat(this.ModulePath, attribute.Path); routes.Add(new Route(attribute.Method.ToUpper(), fullPath, filter, routeDelegate)); } return routes.AsReadOnly(); } private Func GetFilter(string routeMethodName) { var type = this.GetType(); var method = type.GetMethod(routeMethodName + "Filter", BindingFlags.Public | BindingFlags.Instance); if (method == null) { return null; } return (Func)Delegate.CreateDelegate(typeof(Func), this, method.Name); } /// /// Wraps a sync delegate in a delegate that returns a task /// /// Sync delegate /// Task wrapped version private static Func> WrapFunc(Func syncFunc) { return (p, ct) => { var tcs = new TaskCompletionSource(); try { var result = syncFunc.Invoke(p); tcs.SetResult(result); } catch (Exception e) { tcs.SetException(e); } return tcs.Task; }; } } } ================================================ FILE: samples/Nancy.Demo.CustomModule/Views/Index.html ================================================  Hello!

Hello @Model.Name - this was rendered using a custom module type.

================================================ FILE: samples/Nancy.Demo.CustomModule/Web.Debug.config ================================================ ================================================ FILE: samples/Nancy.Demo.CustomModule/Web.Release.config ================================================ ================================================ FILE: samples/Nancy.Demo.CustomModule/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/ApplicationDependencyClass.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; /// /// A module dependency that will have an application lifetime scope. /// public class ApplicationDependencyClass : IApplicationDependency { private readonly DateTime currentDateTime; /// /// Initializes a new instance of the RequestDependencyClass class. /// public ApplicationDependencyClass() { this.currentDateTime = DateTime.Now; } public string GetContent() { return "This is an application level dependency, constructed on: " + this.currentDateTime.ToLongTimeString(); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Content/main.css ================================================ body { background-color: #fcfcfc; font-family: Verdana, Tahoma, "Sans-Serif"; } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Content/scripts.js ================================================ alert("This script was loaded by Nancy"); ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/CustomStatusHandler.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using Nancy.ErrorHandling; public class CustomStatusCodeHandler : IStatusCodeHandler { /// /// Check if the error handler can handle errors of the provided status code. /// /// Status code /// The instance of the current request. /// True if handled, false otherwise public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context) { return statusCode == HttpStatusCode.ImATeapot; } /// /// Handle the error code /// /// Status code /// Current context public void Handle(HttpStatusCode statusCode, NancyContext context) { context.Response = "Response generated by a custom error handler"; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/DefaultRouteMetadataProvider.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System.Collections.Generic; using Nancy.Routing; public class DefaultRouteMetadataProvider : RouteMetadataProvider { // Returns object so you can have you own application-specific // metadata for your routes. protected override MyRouteMetadata GetRouteMetadata(INancyModule module, RouteDescription routeDescription) { // Return the same metadata for all routes in this sample // You would use the Path & Method of the routeDescription // to determine route specific metadata return new MyRouteMetadata(routeDescription.Method, routeDescription.Path); } } public class MyRouteMetadata { public MyRouteMetadata(string method, string path) { this.Method = method; this.Path = path; this.Description = "Lorem ipsum"; this.ValidStatusCodes = new[] { HttpStatusCode.Accepted, HttpStatusCode.OK, HttpStatusCode.Processing }; this.CodeSample = "Get['/'] = x => {\n" + "\treturn View['routes', routeCacheProvider.GetCache()];\n" + "};"; } public string Path { get; set; } public string Method { get; set; } public string Description { get; set; } public IEnumerable ValidStatusCodes { get; set; } public string CodeSample { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/DemoBootstrapper.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; using System.Collections.Generic; using System.Reflection; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Conventions; using Nancy.Cryptography; using Nancy.Diagnostics; using Nancy.Security; using Nancy.Session; using Nancy.TinyIoc; using Nancy.ViewEngines.Razor; public class DemoBootstrapper : DefaultNancyBootstrapper { // Overriding this just to show how it works, not actually necessary as autoregister // takes care of it all. protected override void ConfigureApplicationContainer(TinyIoCContainer existingContainer) { // We don't call base because we don't want autoregister // we just register our one known dependency as an application level singleton existingContainer.Register().AsSingleton(); } // Override with a valid password (albeit a really really bad one!) // to enable the diagnostics dashboard public override void Configure(INancyEnvironment environment) { environment.Diagnostics( enabled: true, password: "password", path: "/_Nancy", cookieName: "__custom_cookie", slidingTimeout: 30, cryptographyConfiguration: CryptographyConfiguration.NoEncryption); environment.Tracing( enabled: true, displayErrorTraces: true); environment.MyConfig("Hello World"); } protected override Func InternalConfiguration { get { return NancyInternalConfiguration.WithOverrides(x => x.ResourceAssemblyProvider = typeof(CustomResourceAssemblyProvider)); } } protected override void ConfigureRequestContainer(TinyIoCContainer existingContainer, NancyContext context) { base.ConfigureRequestContainer(existingContainer, context); existingContainer.Register().AsSingleton(); } protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); Csrf.Enable(pipelines); this.Conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("moo", "Content")); CookieBasedSessions.Enable(pipelines); pipelines.AfterRequest += (ctx) => { var username = ctx.Request.Query.pirate; if (username.HasValue) { ctx.Response = new HereBeAResponseYouScurvyDog(ctx.Response); } }; } } public class MyRazorConfiguration : IRazorConfiguration { public bool AutoIncludeModelNamespace { get { return false; } } public IEnumerable GetAssemblyNames() { return new string[] { }; } public IEnumerable GetDefaultNamespaces() { return new string[] { }; } } public class CustomResourceAssemblyProvider : IResourceAssemblyProvider { private readonly IAssemblyCatalog assemblyCatalog; private IEnumerable filteredAssemblies; public CustomResourceAssemblyProvider(IAssemblyCatalog assemblyCatalog) { this.assemblyCatalog = assemblyCatalog; } public IEnumerable GetAssembliesToScan() { return (this.filteredAssemblies ?? (this.filteredAssemblies = this.assemblyCatalog.GetAssemblies())); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/DependencyModule.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using Nancy.Demo.Hosting.Aspnet.Models; public class DependencyModule : NancyModule { private readonly IApplicationDependency applicationDependency; private readonly IRequestDependency requestDependency; public DependencyModule(IApplicationDependency applicationDependency, IRequestDependency requestDependency) { this.applicationDependency = applicationDependency; this.requestDependency = requestDependency; Get("/dependency", args => { var model = new RatPackWithDependencyText { FirstName = "Bob", ApplicationDependencyText = this.applicationDependency.GetContent(), RequestDependencyText = this.requestDependency.GetContent() }; return View["razor-dependency.cshtml", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/HereBeAResponseYouScurvyDog.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; using System.IO; using System.Text; using Yarrrr; public class HereBeAResponseYouScurvyDog : Response { private readonly string oldResponseOutput; public HereBeAResponseYouScurvyDog(Response response) { this.ContentType = response.ContentType; this.Headers = response.Headers; this.StatusCode = response.StatusCode; using (var memoryStream = new MemoryStream()) { response.Contents.Invoke(memoryStream); this.oldResponseOutput = Encoding.ASCII.GetString(memoryStream.GetBuffer()); } this.Contents = GetContents(this.oldResponseOutput); } protected static Action GetContents(string contents) { return stream => { var writer = new StreamWriter(stream) { AutoFlush = true }; writer.Write(contents.Piratize()); }; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/IApplicationDependency.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { public interface IApplicationDependency { string GetContent(); } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/IRequestDependency.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { public interface IRequestDependency { string GetContent(); } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/MainModule.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; using System.Linq; using Nancy.Configuration; using Nancy.Demo.Hosting.Aspnet.Metadata; using Nancy.Demo.Hosting.Aspnet.Models; using Nancy.Routing; using Nancy.Security; public class MainModule : NancyModule { public MainModule(IRouteCacheProvider routeCacheProvider, INancyEnvironment environment) { Get("/", args => { return View["routes", routeCacheProvider.GetCache()]; }); Get("/texts", args => { return (string)this.Context.Text.Menu.Home; }); Get("/env", args => { return "From nancy environment: " + environment.GetValue().Value; }); Get("/meta", args => { return Negotiate .WithModel(routeCacheProvider.GetCache().RetrieveMetadata()) .WithView("meta"); }); Get("/uber-meta", args => { return Negotiate .WithModel(routeCacheProvider.GetCache().RetrieveMetadata().OfType()) .WithView("uber-meta"); }); Get("/text", args => { var value = (string)this.Context.Text.Menu.Home; return string.Concat("Value of 'Home' resource key in the Menu resource file: ", value); }); Get("/negotiated", args => { return Negotiate .WithModel(new RatPack { FirstName = "Nancy " }) .WithMediaRangeModel("text/html", new RatPack { FirstName = "Nancy fancy pants" }) .WithView("negotiatedview") .WithHeader("X-Custom", "SomeValue"); }); Get("/user/{name}", args => { return (string)args.name; }); Get("/filtered", condition: r => true, action: args => { return "This is a route with a filter that always returns true."; } ); Get("/filtered", condition: r => false, action: args => { return "This is also a route, but filtered out so should never be hit."; } ); Get(@"/(?\d{2,4})/{bar}", args => { return string.Format("foo: {0}
bar: {1}", args.foo, args.bar); }); Get("/test", args => { return "Test"; }); Get("/nustache", args => { return View["nustache", new { name = "Nancy", value = 1000000 }]; }); Get("/dotliquid", args => { return View["dot", new { name = "dot" }]; }); Get("/javascript", args => { return View["javascript.html"]; }); Get("/static", args => { return View["static"]; }); Get("/razor", args => { var model = new RatPack { FirstName = "Frank" }; return View["razor.cshtml", model]; }); Get("/razor-divzero", args => { var model = new { FirstName = "Frank", Number = 22 }; return View["razor-divzero.cshtml", model]; }); Get("/razorError", args => { var model = new RatPack { FirstName = "Frank" }; return View["razor-error.cshtml", model]; }); Get("/razor-simple", args => { var model = new RatPack { FirstName = "Frank" }; return View["razor-simple.cshtml", model]; }); Get("/razor-dynamic", args => { return View["razor.cshtml", new { FirstName = "Frank" }]; }); Get("/razor-cs-strong", args => { return View["razor-strong.cshtml", new RatPack { FirstName = "Frank" }]; }); Get("/razor-vb-strong", args => { return View["razor-strong.vbhtml", new RatPack { FirstName = "Frank" }]; }); Get("/razor2", args => { return new Razor2(); }); Get("/ssve", args => { var model = new RatPack { FirstName = "You" }; return View["ssve.sshtml", model]; }); Get("/viewmodelconvention", args => { return View[new SomeViewModel()]; }); Get("/spark", args => { var model = new RatPack { FirstName = "Bright" }; return View["spark.spark", model]; }); Get("/spark-anon", args => { var model = new { FirstName = "Anonymous" }; return View["anon.spark", model]; }); Get("/json", args => { var model = new RatPack { FirstName = "Andy" }; return this.Response.AsJson(model); }); Get("/xml", args => { var model = new RatPack { FirstName = "Andy" }; return this.Response.AsXml(model); }); Get("/session", args => { var value = Session["moo"] ?? ""; var output = "Current session value is: " + value; if (string.IsNullOrEmpty(value.ToString())) { Session["moo"] = "I've created a session!"; } return output; }); Get("/sessionObject", args => { var value = Session["baa"] ?? "null"; var output = "Current session value is: " + value; if (value.ToString() == "null") { Session["baa"] = new Payload(27, true, "some random string value"); } return output; }); Get("/error", args => { throw new NotSupportedException("This is an exception thrown in a route."); return 500; }); Get("/customErrorHandler", args => { return HttpStatusCode.ImATeapot; }); Get("/csrf", args => { return this.View["csrf", new { Blurb = "CSRF without an expiry using the 'session' token" }]; }); Post("/csrf", args => { this.ValidateCsrfToken(); return string.Format("Hello {0}!", this.Request.Form.Name); }); Get("/csrfWithExpiry", args => { // Create a new one because we have an expiry to check this.CreateNewCsrfToken(); return this.View["csrf", new { Blurb = "You have 20 seconds to submit the page.. TICK TOCK :-)" }]; }); Post("/csrfWithExpiry", args => { this.ValidateCsrfToken(TimeSpan.FromSeconds(20)); return string.Format("Hello {0}!", this.Request.Form.Name); }); Get("/viewNotFound", args => { return View["I-do-not-exist"]; }); Get("/fileupload", args => { return View["FileUpload", new { Posted = "Nothing" }]; }); Post("/fileupload", args => { var file = this.Request.Files.FirstOrDefault(); string fileDetails = "Nothing"; if (file != null) { fileDetails = string.Format("{3} - {0} ({1}) {2}bytes", file.Name, file.ContentType, file.Value.Length, file.Key); } return View["FileUpload", new { Posted = fileDetails }]; }); Get( name: "NamedRoute", path: "/namedRoute", action: args => { return "I am a named route!"; }); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Metadata/MainMetadataModule.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Metadata { using Nancy.Metadata.Modules; public class MainMetadataModule : MetadataModule { public MainMetadataModule() { Describe["NamedRoute"] = desc => { return new MyUberRouteMetadata(desc.Method, desc.Path) { SuperDescription = "Returns the string \"I am a named route!\"", CodeSample = "Get[\"NamedRoute\", \"/namedRoute\"] = _ => \"I am a named route!\";" }; }; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Metadata/MyUberRouteMetadata.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Metadata { public class MyUberRouteMetadata : MyRouteMetadata { public MyUberRouteMetadata(string method, string path) : base(method, path) { } public string SuperDescription { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Models/Payload.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Models { using System; [Serializable] public class Payload : IEquatable { public int IntValue { get; private set; } public bool BoolValue { get; private set; } public string StringValue { get; private set; } /// /// Initializes a new instance of the class. /// public Payload(int intValue, bool boolValue, string stringValue) { this.IntValue = intValue; this.BoolValue = boolValue; this.StringValue = stringValue; } public bool Equals(Payload other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return other.IntValue == this.IntValue && other.BoolValue.Equals(this.BoolValue) && Equals(other.StringValue, this.StringValue); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } return obj.GetType() == typeof(Payload) && this.Equals((Payload)obj); } public override int GetHashCode() { unchecked { var result = this.IntValue; result = (result * 397) ^ this.BoolValue.GetHashCode(); result = (result * 397) ^ (this.StringValue != null ? this.StringValue.GetHashCode() : 0); return result; } } public static bool operator ==(Payload left, Payload right) { return Equals(left, right); } public static bool operator !=(Payload left, Payload right) { return !Equals(left, right); } public override string ToString() { return string.Format("{0},{1},{2}", this.StringValue, this.IntValue, this.BoolValue); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Models/RatPack.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Models { public class RatPack { public string FirstName { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Models/RatPackWithDependencyText.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Models { public class RatPackWithDependencyText : RatPack { public string ApplicationDependencyText { get; set; } public string RequestDependencyText { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Models/Razor2.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Models { public class Razor2 { public string FirstName { get; set; } public string NotNullOne { get; set; } public string NotNullTwo { get; set; } public string NullOne { get; set; } public string NullTwo { get; set; } public bool FalseBool { get; set; } public bool TrueBool { get; set; } public Razor2() { this.FirstName = "Razor2"; this.NotNullOne = "NotNullOne"; this.NotNullTwo = "NotNullTwo"; this.TrueBool = true; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Models/SomeViewModel.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet.Models { public class SomeViewModel { } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/MyConfig.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { /// /// Sample custom configuration type. It is good practise (but not required) /// to make your config objects immutable. /// public class MyConfig { public MyConfig(string value) { this.Value = value; } public string Value { get; private set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/MyConfigExtensions.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using Nancy.Configuration; /// /// Illustrates how you can create custom environment configurations by hanging /// extension methods on INancyEnvironment and sticking custom configuration /// objects into environment. /// public static class MyConfigExtensions { public static void MyConfig(this INancyEnvironment environment, string value) { environment.AddValue( typeof(MyConfig).FullName, // Using the full type name of the type to avoid collisions new MyConfig(value)); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Nancy.Demo.Hosting.Aspnet.csproj ================================================  Debug AnyCPU 2.0 {E127FED3-01C0-41BA-BF83-D8DCDD827D6A} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Hosting.Aspnet Nancy.Demo.Hosting.Aspnet v4.5 true 4.0 enabled enabled false true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Hosting.Aspnet.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Hosting.Aspnet.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false ..\..\packages\Microsoft.CodeAnalysis.Common.1.1.1\lib\net45\Microsoft.CodeAnalysis.dll True ..\..\packages\Microsoft.CodeAnalysis.CSharp.1.1.1\lib\net45\Microsoft.CodeAnalysis.CSharp.dll True ..\..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll True ..\..\packages\System.Reflection.Metadata.1.1.0\lib\dotnet5.2\System.Reflection.Metadata.dll True Designer Designer Web.config Web.config Properties\SharedAssemblyInfo.cs True True Menu.resx {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {DC8AAA8B-7FD0-47C4-9413-3CC94088BCC4} Nancy.Metadata.Modules {B795886D-9D70-45B1-BFB5-AD54CBC9A447} Nancy.ViewEngines.DotLiquid {A1A9E636-A37C-4170-B2E4-99D4F69B415D} Nancy.ViewEngines.Nustache {2c6f51df-015c-4db6-b44c-0e5e4f25e2a9} Nancy.ViewEngines.Razor {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy ResXFileCodeGenerator Menu.Designer.cs 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) True False 40965 /NancyTest/ http://localhost:59017/ False False False ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Piratizer4000.cs ================================================ namespace Yarrrr { using System; using System.Collections.Generic; using System.Linq; /// /// Pirate dictionary be plundered from Davie Reed ( http://nifty.stanford.edu/2004/TalkLikeAPirate/pirate3.html ) /// Yarrr! /// public static class HereBePiratesYarrr { public static string ToSentenceCase(this string input) { return String.Format("{0}{1}", input.Substring(0, 1).ToUpper(), input.Substring(1)); } private static readonly Dictionary lowerSubstitutions; private static readonly Dictionary sentenceCaseSubstitutions; static HereBePiratesYarrr() { lowerSubstitutions = new Dictionary { { "hello", "ahoy" }, { "hi", "yo-ho-ho" }, { "pardon me", "avast" }, { "excuse me", "arrr" }, { "yes", "aye" }, { "my", "me" }, { "friend", "me bucko" }, { "sir", "matey" }, { "madam", "proud beauty" }, { "miss", "comely wench" }, { "stranger", "scurvy dog" }, { "officer", "foul blaggart" }, { "where", "whar" }, { "is", "be" }, { "are", "be" }, { "am", "be" }, { "the", "th'" }, { "you", "ye" }, { "your", "yer" }, { "tell", "be tellin'" }, { "know", "be knowin'" }, { "how far", "how many leagues" }, { "old", "barnacle-covered" }, { "attractive", "comely" }, { "happy", "grog-filled" }, { "quickly", "smartly" }, { "nearby", "broadside" }, { "restroom", "head" }, { "restaurant", "galley" }, { "hotel", "fleabag inn" }, { "pub", "Skull & Scuppers" }, { "mall", "market" }, { "bank", "buried treasure" }, { "die", "visit Davey Jones' Locker" }, { "died", "visited Davey Jones' Locker" }, { "kill", "keel-haul" }, { "killed", "keel-hauled" }, { "sleep", "take a caulk" }, { "stupid", "addled" }, { "after", "aft" }, { "stop", "belay" }, { "nonsense", "bilge" }, { "ocean", "briny deep" }, { "song", "shanty" }, { "money", "doubloons" }, { "food", "grub" }, { "nose", "prow" }, { "leave", "weigh anchor" }, { "cheat", "hornswaggle" }, { "forward", "fore" }, { "child", "sprog" }, { "children", "sprogs" }, { "sailor", "swab" }, { "lean", "careen" }, { "find", "come across" }, { "mother", "dear ol' mum, bless her black soul" }, { "drink", "barrel o' rum" }, { "of", "o'" }, { "!", "YARRR!" }, }; sentenceCaseSubstitutions = new Dictionary(lowerSubstitutions.Count); foreach (var substitution in lowerSubstitutions) { sentenceCaseSubstitutions.Add(substitution.Key.ToSentenceCase(), substitution.Key.ToSentenceCase()); } } public static string Piratize(this string boringEnglishString) { // TODO - turn this into a non-horrible regex ;-) return lowerSubstitutions.Aggregate(boringEnglishString, (current, substitution) => current.Replace(substitution.Key, substitution.Value)); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/PngSerializer.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using Nancy.Responses.Negotiation; /// /// If you request /negotiated with an accept header with the value image/png you will get an image back /// public class PngSerializer : ISerializer { private readonly IRootPathProvider rootPathProvider; public PngSerializer(IRootPathProvider rootPathProvider) { this.rootPathProvider = rootPathProvider; } /// /// Whether the serializer can serialize the content type /// /// Content type to serialise /// True if supported, false otherwise public bool CanSerialize(MediaRange mediaRange) { return mediaRange.Matches("image/png"); } /// /// Gets the list of extensions that the serializer can handle. /// /// An of extensions if any are available, otherwise an empty enumerable. public IEnumerable Extensions { get { yield return "png"; } } /// /// Serialize the given model with the given contentType /// /// Content type to serialize into /// Model to serialize /// Output stream to serialize to /// Serialised object public void Serialize(MediaRange mediaRange, TModel model, Stream outputStream) { var path = Path.Combine(this.rootPathProvider.GetRootPath(), "content/face.png"); var face = Image.FromFile(path); face.Save(outputStream, ImageFormat.Png); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/RequestDependencyClass.cs ================================================ namespace Nancy.Demo.Hosting.Aspnet { using System; /// /// A module dependency that will have a per-request lifetime scope. /// public class RequestDependencyClass : IRequestDependency { private readonly DateTime currentDateTime; /// /// Initializes a new instance of the RequestDependencyClass class. /// public RequestDependencyClass() { this.currentDateTime = DateTime.Now; } public string GetContent() { return "This is a per-request dependency, constructed on: " + this.currentDateTime.ToLongTimeString(); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Resources/Menu.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Nancy.Demo.Hosting.Aspnet.Resources { using System.CodeDom.Compiler; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Resources; using System.Runtime.CompilerServices; /// /// 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. [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [DebuggerNonUserCode()] [CompilerGenerated()] internal class Menu { private static ResourceManager resourceMan; private static CultureInfo resourceCulture; [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Menu() { } /// /// Returns the cached ResourceManager instance used by this class. /// [EditorBrowsable(EditorBrowsableState.Advanced)] internal static ResourceManager ResourceManager { get { if (ReferenceEquals(resourceMan, null)) { ResourceManager temp = new ResourceManager("Nancy.Demo.Hosting.Aspnet.Resources.Menu", typeof(Menu).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [EditorBrowsable(EditorBrowsableState.Advanced)] internal static CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to This is the home link. /// internal static string Home { get { return ResourceManager.GetString("Home", resourceCulture); } } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Resources/Menu.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 This is the home link ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/FileUpload.sshtml ================================================  Nancy File Posting Demo

You uploaded: @Model.Posted

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/anon.spark ================================================  Spark View Engine Demo

The name's Spark, ${Model.FirstName} Spark

This is a sample Spark view!

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/csrf.cshtml ================================================  CSRF Demo

CSRF Test

@Model.Blurb

@Html.AntiForgeryToken();
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/dot.liquid ================================================  DotLiquid View Engine Demo

The name's Liquid, {{ model.name | upcase }} Liquid

This is a sample DotLiquid view!

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/interactive-diags-methods.sshtml ================================================  Interactive Diagnostics @Each.Methods
@Current.ReturnType : @Current.MethodName

@EndEach ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/interactive-diags-results.sshtml ================================================  Interactive Diagnostics
@Model.Json
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/interactive-diags.sshtml ================================================  Interactive Diagnostics @Each @Current.Name @EndEach ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/javascript.html ================================================  Nancy - Loading Javascripts The script on this page was loaded using Nancy. ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/meta.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase> Route metadata

Route metadata

Below is a list of route metadata that was generated from a custom IRouteMetadataProvider implementation

@foreach (var meta in @Model) {

(@meta.Method) @meta.Path

Description:

@meta.Description

Valid return status codes:

@string.Join(", ", @meta.ValidStatusCodes)

Code sample:
@meta.CodeSample
}
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/negotiatedview.cshtml ================================================  Negotiation Demo

Hello @Model.FirstName

This is a text/html representation of a RatPack model, you should be seeing this because you requested text/html either manually or via your browser. Try using Fiddler to hit this url and request JSON, or add .json to the end of this url.

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/nustache.nustache ================================================ {{>nustachePartial}} You have just won ${{value}}! ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/nustachePartial.nustache ================================================ Hello {{name}} ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-dependency.cshtml ================================================  Razor View Engine Demo

Hello @Model.FirstName

This is a Razor view..

@Model.ApplicationDependencyText

@Model.RequestDependencyText

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-divzero.cshtml ================================================ @{ Layout = "razor-layout.cshtml"; } @section Header { }

Hello @Model.FirstName

This is a sample Razor view! @(Model.Number / 0)

@section Footer {

This is footer content!

Face } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-error.cshtml ================================================ @{ Layout = "razor-layout-error.cshtml"; } @section Header { }

Hello @Model.FirstName

This is a sample Razor view!

@section Footer {

This is footer content!

Face } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-layout-error.cshtml ================================================  Razor View Engine Demo - @Model.FirstName @RenderSection("Header")
@RenderBody()
@ThisDoesntExist()
@RenderSection("Optional", false)
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-layout.cshtml ================================================  Razor View Engine Demo - @Model.FirstName @RenderSection("Header")
@RenderBody()
@RenderSection("Optional", false)
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-simple.cshtml ================================================  Razor View Engine Demo

Hello @Model.FirstName

This is a sample Razor view!

Face ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-strong.cshtml ================================================ @model Nancy.Demo.Hosting.Aspnet.Models.RatPack Razor View Engine Demo

Hello @Model.FirstName

This is a strongly typed csharp razor view!

Face ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor-strong.vbhtml ================================================ @ModelType Nancy.Demo.Hosting.Aspnet.Models.RatPack Razor View Engine Demo

Hello @Model.FirstName

This is a strongly typed vb razor view!

Face ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor.cshtml ================================================ @{ Layout = "razor-layout.cshtml"; } @section Header { }

Hello @Model.FirstName

This is a sample Razor view!

@section Footer {

This is footer content!

Face } ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/razor2.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase Razor View Engine Demo

Hello @Model.FirstName

This is a sample Razor2 view!

Test link to @Model.FirstName Test link to @Model.FirstName Face
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/routes.cshtml ================================================  Routes

Routes

Below is a list of all of the currently registered routes in this application

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/someview.cshtml ================================================  Razor View Engine Demo

Model: @Model.GetType().Name

View: SomeView

This is a sample of using the name of the model type to locate the view to render!

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/spark.spark ================================================  Spark View Engine Demo

The name's Spark, ${Model.FirstName} Spark

This is a sample Spark view!

Face ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/ssve.sshtml ================================================  Super Simple View Engine Demo

SSVE - so simple, even @Model.FirstName can do it.

This is a sample SSVE view!

Face ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/static.html ================================================  Static View Engine Demo

Hello Tina

This is a sample static view!

================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Views/uber-meta.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase> Route metadata

Route metadata

Below is a list of route metadata that was declared within a metadata module.

@foreach (var meta in @Model) {

(@meta.Method) @meta.Path

Super Description:

@meta.SuperDescription

Description:

@meta.Description

Valid return status codes:

@string.Join(", ", @meta.ValidStatusCodes)

Code sample:
@meta.CodeSample
}
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/Web.config ================================================ 
================================================ FILE: samples/Nancy.Demo.Hosting.Aspnet/packages.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/AppConfiguration.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { public class AppConfiguration : IAppConfiguration { public Logging Logging { get; set; } public Smtp Smtp { get; set; } } public class LogLevel { public string Default { get; set; } public string System { get; set; } public string Microsoft { get; set; } } public class Logging { public bool IncludeScopes { get; set; } public LogLevel LogLevel { get; set; } } public class Smtp { public string Server { get; set; } public string User { get; set; } public string Pass { get; set; } public string Port { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/DemoBootstrapper.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { using Nancy; using Nancy.TinyIoc; public class DemoBootstrapper : DefaultNancyBootstrapper { private readonly IAppConfiguration appConfig; public DemoBootstrapper() { } public DemoBootstrapper(IAppConfiguration appConfig) { this.appConfig = appConfig; } protected override void ConfigureApplicationContainer(TinyIoCContainer container) { base.ConfigureApplicationContainer(container); container.Register(appConfig); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/HomeModule.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { using ModelBinding; public class HomeModule : NancyModule { public HomeModule(IAppConfiguration appConfig) { Get("/", args => "Hello from Nancy running on CoreCLR"); Get("/conneg/{name}", args => new Person() { Name = args.name }); Get("/smtp", args => { return new { appConfig.Smtp.Server, appConfig.Smtp.User, appConfig.Smtp.Pass, appConfig.Smtp.Port }; }); Post("/", args => { var person = this.BindAndValidate(); if (!this.ModelValidationResult.IsValid) { return 422; } return person; }); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/IAppConfiguration.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { public interface IAppConfiguration { Logging Logging { get; } Smtp Smtp { get; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/Nancy.Demo.Hosting.Kestrel.csproj ================================================ netcoreapp2.0 portable Nancy.Demo.Hosting.Kestrel Exe ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/Person.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { public class Person { public string Name { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/PersonValidator.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { using FluentValidation; public class PersonValidator : AbstractValidator { public PersonValidator() { this.RuleFor(x => x.Name).NotEmpty(); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/Program.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { using System.IO; using Microsoft.AspNetCore.Hosting; public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .UseStartup() .Build(); host.Run(); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:59916/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/Startup.cs ================================================ namespace Nancy.Demo.Hosting.Kestrel { using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Nancy.Owin; public class Startup { private readonly IConfiguration config; public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .SetBasePath(env.ContentRootPath); config = builder.Build(); } public void Configure(IApplicationBuilder app) { var appConfig = new AppConfiguration(); ConfigurationBinder.Bind(config, appConfig); app.UseOwin(x => x.UseNancy(opt => opt.Bootstrapper = new DemoBootstrapper(appConfig))); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Kestrel/appsettings.json ================================================ { "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Verbose", "System": "Information", "Microsoft": "Information" } }, "Smtp": { "Server": "0.0.0.1", "User": "user@company.com", "Pass": "123456789", "Port": "25" } } ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/MainModule.cs ================================================ namespace Nancy.Demo.Hosting.Owin { using System.Collections.Generic; using Nancy.Owin; public class MainModule : NancyModule { public MainModule() { Get("", args => Root(args)); } private object Root(dynamic o) { var env = GetOwinEnvironmentValue>(this.Context.Items, NancyMiddleware.RequestEnvironmentKey); if (env == null) { return "Not running on owin host"; } var requestMethod = GetOwinEnvironmentValue(env, "owin.RequestMethod"); var requestPath = GetOwinEnvironmentValue(env, "owin.RequestPath"); var owinVersion = GetOwinEnvironmentValue(env, "owin.Version"); var statusMessage = string.Format("You made a {0} request to {1} which runs on OWIN {2}.", requestMethod, requestPath, owinVersion); return View["Root", new Models.Index {StatusMessage = statusMessage}]; } private static T GetOwinEnvironmentValue(IDictionary env, string name, T defaultValue = default(T)) { object value; return env.TryGetValue(name, out value) && value is T ? (T)value : defaultValue; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Models/Index.cs ================================================ namespace Nancy.Demo.Hosting.Owin.Models { public class Index { public string StatusMessage { get; set; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Nancy.Demo.Hosting.Owin.csproj ================================================  Debug AnyCPU 2.0 {7959D79C-9E2B-4871-9470-6A6B527ABE5E} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.Hosting.Owin Nancy.Demo.Hosting.Owin v4.5 false 4.0 enabled enabled false true full false bin\ TRACE;DEBUG prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Hosting.Owin.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Hosting.Owin.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True ..\..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll True ..\..\packages\Owin.1.0\lib\net40\Owin.dll True False ..\..\packages\Spark.1.7.0.0\lib\NET40\Spark.dll Designer Web.config Web.config Properties\SharedAssemblyInfo.cs {d6b2480c-2a52-4d02-9477-d387d7a486e5} Nancy.Owin {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) True ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Startup.cs ================================================ namespace Nancy.Demo.Hosting.Owin { using global::Owin; public class Startup { public void Configuration(IAppBuilder app) { app.UseNancy(); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Views/Root.spark ================================================  Nancy OWIN Host Demo

Nancy OWIN Host Demo

This is a Spark view rendered via the OWIN hosting. ${Model.StatusMessage}

================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Owin/packages.config ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Self/DemoBootstrapper.cs ================================================ namespace Nancy.Demo.Hosting.Self { using Nancy; using Nancy.Diagnostics; public class DemoBootstrapper : DefaultNancyBootstrapper { public override void Configure(Nancy.Configuration.INancyEnvironment environment) { environment.Diagnostics( enabled: true, password: "password"); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Self/Models/Index.cs ================================================ namespace Nancy.Demo.Hosting.Self.Models { public class Index { public string Name { get; set; } public string Posted { get; set; } public Index() { this.Posted = "Nothing :-("; } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Self/Nancy.Demo.Hosting.Self.csproj ================================================  Debug x86 {0B3EA40E-F7D8-4E14-A30F-1536F41B62D1} Exe Properties Nancy.Demo.Hosting.Self Nancy.Demo.Hosting.Self v4.5 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset false true x86 pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset false Nancy.Demo.Hosting.Self.Program true bin\x86\MonoDebug\ DEBUG;TRACE full x86 bin\Debug\Nancy.Demo.Hosting.Self.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\x86\MonoRelease\ TRACE true pdbonly x86 bin\Release\Nancy.Demo.Hosting.Self.exe.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Properties\SharedAssemblyInfo.cs {AA7F66EB-EC2C-47DE-855F-30B3E6EF2134} Nancy.Hosting.Self {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy Designer Designer Always Always ================================================ FILE: samples/Nancy.Demo.Hosting.Self/Program.cs ================================================ namespace Nancy.Demo.Hosting.Self { using System; using System.Diagnostics; using Nancy.Hosting.Self; class Program { static void Main() { using (var nancyHost = new NancyHost(new Uri("http://localhost:8888/nancy/"), new Uri("http://127.0.0.1:8898/nancy/"), new Uri("http://localhost:8889/nancytoo/"))) { nancyHost.Start(); Console.WriteLine("Nancy now listening - navigating to http://localhost:8888/nancy/. Press enter to stop"); try { Process.Start("http://localhost:8888/nancy/"); } catch (Exception) { } Console.ReadKey(); } Console.WriteLine("Stopped. Good bye!"); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Self/README.txt ================================================  ================================================ FILE: samples/Nancy.Demo.Hosting.Self/TestModule.cs ================================================ namespace Nancy.Demo.Hosting.Self { using System.Linq; using Nancy.Demo.Hosting.Self.Models; public class TestModule : NancyModule { public TestModule() { Get("/", args => { return View["staticview", this.Request.Url]; }); Get("/testing", args => { return View["staticview", this.Request.Url]; }); Get("/fileupload", args => { var model = new Index() { Name = "Boss Hawg" }; return View["FileUpload", model]; }); Post("/fileupload", args => { var model = new Index() { Name = "Boss Hawg" }; var file = this.Request.Files.FirstOrDefault(); string fileDetails = "None"; if (file != null) { fileDetails = string.Format("{3} - {0} ({1}) {2}bytes", file.Name, file.ContentType, file.Value.Length, file.Key); } model.Posted = fileDetails; return View["FileUpload", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.Hosting.Self/Views/FileUpload.spark ================================================  Nancy Self Host Demo

Hello ${Model.Name}!

This is a Spark view rendered via the self hosting.

You uploaded: ${Model.Posted}

================================================ FILE: samples/Nancy.Demo.Hosting.Self/Views/staticview.html ================================================  Nancy - Static view served by self-host

Static view served by self-host

This view was served by the Nancy self-host.

  • Scheme: @Model.Scheme
  • HostName: @Model.HostName
  • Port: @Model.Port
  • BasePath: @Model.BasePath
  • Path: @Model.Path
http://localhost:8888/nancy/
http://localhost:8888/nancy/testing
http://127.0.0.1:8898/nancy/
http://127.0.0.1:8898/nancy/testing
http://localhost:8889/nancytoo/
http://localhost:8889/nancytoo/testing
================================================ FILE: samples/Nancy.Demo.Hosting.Self/app.config ================================================ ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Content/blog.css ================================================ body { background: #f2f2f2 url('/content/images/body.png') repeat scroll top center /* Credit: Pixel Oplosan (http://www.pixeloplosan.com) */; margin: 0; color: #777777; } a:link, a:visited, a:hover { color: #6c83ad; text-decoration: none; } a:hover { color: #45597f; text-decoration: underline; } a img { border-width: 0; } #navbar-iframe, #LinkList1 h2, .feed-links { display: none; } .firstline { text-transform: uppercase; font-size: 11px; } h1, h2, h3, h4, h5, h6 { font-family: Arial, Helvetica, sans-serif; font-weight: normal; clear: both; } h1 { font-size: 28px; } h2 { font-size: 24px; } h3 { font-size: 20px; } h4 { font-size: 12px; } h5 { font-size: 10px; } h6 { font-size: 8px; } hr { background-color: #777777; border: 0; height: 1px; margin-bottom: 2px; clear: both; } pre { overflow: auto; padding: 10px; background: #635c5c; color: #fff; margin-bottom: 0; } /* =================== Header =================== */ #header-wrapper { width: 100%; overflow: hidden; padding: 0; } #header { padding-bottom: 50px; } #header .caption { font-size: 16px; font-family: Garamond, Georgia, 'Times New Roman', serif; font-style: italic; } #header h1 { margin: 0; padding: 10px 0 0 0; font: normal normal 36px Lobster; color: #666; } #header .description { margin: 0; padding: 0; font: normal normal 10px Verdana, Geneva, sans-serif; } #header a { color: #666; text-decoration: none; } #header a:hover { color: #111; } /* =================== Outer-Wrapper =================== */ #outer-wrapper { width: 1074px; margin: 0 auto; padding: 0; text-align: left; font: normal normal 12px Helvetica, Arial, Sans; } #content-wrapper { width: 100%; } /* =================== Set cloud =================== */ #mountain { background: url('/content/images/mountain.png') no-repeat 205px bottom; height: 200px; width: 100%; margin: 0 auto -1px auto; } #cover-cloud { width: 100%; min-width: 1074px; height: 200px; position: absolute; z-index: 100; left: 0; } #cloud { background: url('/content/images/cloud1.png') repeat-x 0 0; position: absolute; width: 100%; height: 188px; z-index: 200; } #cloud2 { background: url('/content/images/cloud2.png') repeat-x 0 0; position: absolute; width: 100%; height: 125px; z-index: 300; } #cloud3 { background: url('/content/images/cloud3.png') repeat-x 0 0; position: absolute; width: 100%; height: 46px; z-index: 400; } /* =================== Navigation =================== */ .PageList h2 { display: none; } .PageList ul { list-style: none; margin: 10px 0 0 0; line-height: 2.3em; } .PageList ul li { padding: 5px 0; color: #666; background: none; border: none; } .PageList ul li a { font: normal normal 18px 'Questrial'; } .PageList .selected a { color: #111; font-weight: normal!important; } /* =================== Primary Content =================== */ #main-wrapper { float: left; width: 604px; background: url('/content/images/post-head.png') no-repeat center top; } #main { background: url('/content/images/post-midd.png') repeat-y center top; .background-position:left top; } #main .widget { background: url('/content/images/post-foot.png') no-repeat center bottom; background-position: left bottom; margin: 30px 0; padding: 0 20px 20px 20px; } .main .Blog { border-bottom-width: 0; } .post-header-line-1 { clear: both; font-size: 11px; text-align: left; line-height: 1.6em; } .hentry-top { font-size: 8px; text-transform: uppercase; text-align: left; } .post { margin: 0 0 50px 0; } .post h3 { margin: 0; padding: 0; } .post h3 a, .post h3 a:visited, .post h3 strong { width: 100%; font: normal normal 35px 'questrial'; float: left; text-decoration: none; color: #6c83ad; text-align: left; padding: 5px 0 0 0; margin-bottom: 20px; } .post h3 strong, .post h3 a:hover { color: $titlecolor; } .post-body { margin: 0 0 .75em; line-height: 1.8em; } .post-body p { padding-top: 0; margin-top: 0; } .post-body blockquote { line-height: 1.8em; border-left: 1px solid #ddd; padding: 10px; color: #828282; } .post blockquote { margin: 0 20px; } .post blockquote p { margin: .75em 0; } .post-footer { margin: 30px 0 0; font-size: 11px; padding: 0; color: #b0b0b0!important; } .post-footer a { color: #b0b0b0; } .post-footer-line-2 { margin-top: 5px; } .comment-link { margin-left: .6em; } .post img, table.tr-caption-container { max-width: 520px; height: auto; margin: 0; overflow: hidden; } .tr-caption-container img { border: none; padding: 0; } .jump-link a { font-size: 11px; border-bottom: 1px dotted #cc2610; } .jump-link { margin-top: 20px; } /* ======================= Comments Content ======================= */ #comments h4 { font: normal normal 35px 'questrial'; font-size: 16px !important; color: #6c83ad; clear: both; } #comments { margin: 0; padding: 0; float: left; clear: left; .width:565px; } .comment-header .user { font-size: 14px; font-weight: bold; } .comment-header .datetime { font-size: 11px; } .comment-thread li .comment-block { border-bottom: 1px dotted #e2e2e2; padding-bottom: 10px; .margin-left:10px; } .comment-content { margin: 0; padding: 0; line-height: 1.8em!important; } .comments blockquote { margin-bottom: 20px; padding: 10px 0; } #comment-holder .comment-block .comment-actions a { font-size: 11px; margin-right: 20px; } #comment-holder .avatar-image-container { background: transparent; overflow: hidden; .margin-left:-35px; } #comment-holder .avatar-image-container img { border: medium none; } #comments-block.avatar-comment-indent { position: relative; margin: 0 auto; } /* =================== Page Navigation =================== */ #blog-pager-newer-link { float: right; } #blog-pager-older-link { float: left; margin-right: 10px; } #blog-pager { font: normal normal 18px 'Questrial'; border-top: 3px double #e2e2e2; padding-top: 20px; clear: both; } /* =================== Secondary Content =================== */ #left-content { float: left; width: 200px; padding-top: 20px; } #sidebar-wrapper { float: right; width: 270px; padding-top: 20px; } .sidebar, .header { color: $sidebartextcolor; line-height: 1.8em; padding: 0 20px; } .sidebar h2 { font: normal normal 20px Lobster; margin: 0 0 20px 0; padding: 4px 0; color: #555; } .sidebar ul { list-style: none; margin: 0 0 0; padding: 0 0 0; line-height: 1.8em; } .sidebar li { margin: 0; } .sidebar .widget { margin: 0 0 20px 0; padding: 0 0 15px 0; clear: both; } .sidebar .Label li { float: left; width: 45%; margin-right: 5%; } .sidebar blockquote { margin: 0; padding: 15px 0; font-size: 14px; font-style: italic; border-top: 1px solid #e2e2e2; border-bottom: 1px solid #e2e2e2; font-family: georgia, 'times new roman', serif; } .sidebar .PopularPosts .item-snippet { font-size: 11px; } .sidebar .PopularPosts .item-title { font-weight: bold; } .sidebar .Attribution .widget-content { text-align: left!important; color: #bbb; text-shadow: 1px 1px 0 #fff; } .sidebar .Attribution .widget-content a { text-transform: uppercase; font-weight: bold; font-size: 11px; color: #aaa; } .sidebar .Attribution .widget-content a:hover { color: #000; } /* =================== Footer =================== */ #credit-wrapper { margin: 0 auto; padding: 30px 0 0 20px; clear: both; color: #bbb; line-height: 1.6em; text-shadow: 1px 1px 0 #fff; } .credit span { text-transform: uppercase; font-weight: bold; font-size: 11px; } .credit span em { font-weight: normal; text-transform: lowercase; font-style: italic; font-family: georgia, 'times new roman', serif; } .credit a, .footer a:visited { color: #aaa; } .credit a:hover { color: #000; text-decoration: underline; } ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Content/js/jquery.spritely-0.6.js ================================================ /* * jQuery spritely 0.6 * http://spritely.net/ * * Documentation: * http://spritely.net/documentation/ * * Copyright 2010-2011, Peter Chater, Artlogic Media Ltd, http://www.artlogic.net/ * Dual licensed under the MIT or GPL Version 2 licenses. * * Change history: * Version 0.6 * - added events to the .sprite() method: on_first_frame, on_last_frame, on_frame: * $('#sprite').sprite({ * fps: 9, * no_of_frames: 24, * on_first_frame: function(obj) { * obj.spState(1); // change to state 1 (first row) on frame 1 * }, * on_last_frame: function(obj) { * obj.spStop(); // stop the animation on the last frame * }, * on_frame: { * 8: function(obj) { * obj.spState(2); // change to state 2 (row 2) on frame 8 * }, * 16: function(obj) { * obj.spState(3); // change to state 3 (row 3) on frame 16 * } * } * }); * - added start_at_frame: $('#sprite').sprite({fps: 9, no_of_frames: 24, start_at_frame: 8}); * Version 0.5 * - added a 'destroy()' method which will stop the element's sprite behaviour, without actually removing the element. Example: $('#my_sprite').destroy() * Version 0.4 * - add up/down for 'pan' method. * - added 'dir' option for Sprites. This means a Sprite can be played in reverse. * - removed alert message regarding jQuery.draggable (now uses console.log, if available) * Version 0.3b * - added lockTo method for locking sprites to background images. $('#sprite').lockTo('#background, {'left': 380, 'top': -60, 'bg_img_width': 1110}); * Version 0.2.1 * - animate function will stop cycling after play_frames has completed * Version 0.2 beta * - added isDraggable method (requires jquery-ui) $('#sprite').sprite().isDraggable({start: null, stop: function() {alert('Ouch! You dropped me!')}); * - sprites may be set to play a limited number of frames when instantiated, e.g. $('#sprite').sprite({fps: 9, no_of_frames: 3, play_frames: 30}) * - sprite speed may be controlled at any point by setting the frames-per-second $('#sprite').fps(20); * - sprites with multiple rows of frames may have there 'state' changed, e.g. to make the second row of frames * active, use: $('#sprite').spState(2); - to return to the first row, use $('#sprite').spState(1); * - background element speed may be controlled at any point with .spSpeed(), e.g. $('#bg1').spSpeed(10) * - background elements may be set to a depth where 100 is the viewer (up close) and 0 is the horizon, e.g.: * $('#bg1').pan({fps: 30, speed: 2, dir: 'left', depth: 30}); * $('#bg2').pan({fps: 30, speed: 3, dir: 'left', depth: 70}); * relative speed of backgrounds may now be set in a single action with $('#bg1, #bg2').spRelSpeed(20); * which will make elements closer to the horizon (lower depths) move slower than closer elements (higher depths) */ (function($) { $._spritely = { // shared methods and variables used by spritely plugin animate: function(options) { var el = $(options.el); var el_id = el.attr('id'); if (!$._spritely.instances[el_id]) { return this; } options = $.extend(options, $._spritely.instances[el_id] || {}); if (options.play_frames && !$._spritely.instances[el_id]['remaining_frames']) { $._spritely.instances[el_id]['remaining_frames'] = options.play_frames + 1; } if (options.type == 'sprite' && options.fps) { var frames; var animate = function(el) { var w = options.width, h = options.height; if (!frames) { frames = []; total = 0 for (var i = 0; i < options.no_of_frames; i ++) { frames[frames.length] = (0 - total); total += w; } } if ($._spritely.instances[el_id]['current_frame'] == 0) { if (options.on_first_frame) { options.on_first_frame(el); } } else if ($._spritely.instances[el_id]['current_frame'] == frames.length - 1) { if (options.on_last_frame) { options.on_last_frame(el); } } if (options.on_frame && options.on_frame[$._spritely.instances[el_id]['current_frame']]) { options.on_frame[$._spritely.instances[el_id]['current_frame']](el); } if (options.rewind == true) { if ($._spritely.instances[el_id]['current_frame'] <= 0) { $._spritely.instances[el_id]['current_frame'] = frames.length - 1; } else { $._spritely.instances[el_id]['current_frame'] = $._spritely.instances[el_id]['current_frame'] - 1; }; } else { if ($._spritely.instances[el_id]['current_frame'] >= frames.length - 1) { $._spritely.instances[el_id]['current_frame'] = 0; } else { $._spritely.instances[el_id]['current_frame'] = $._spritely.instances[el_id]['current_frame'] + 1; } } var yPos = $._spritely.getBgY(el); el.css('background-position', frames[$._spritely.instances[el_id]['current_frame']] + 'px ' + yPos); if (options.bounce && options.bounce[0] > 0 && options.bounce[1] > 0) { var ud = options.bounce[0]; // up-down var lr = options.bounce[1]; // left-right var ms = options.bounce[2]; // milliseconds el .animate({top: '+=' + ud + 'px', left: '-=' + lr + 'px'}, ms) .animate({top: '-=' + ud + 'px', left: '+=' + lr + 'px'}, ms); } } if ($._spritely.instances[el_id]['remaining_frames'] && $._spritely.instances[el_id]['remaining_frames'] > 0) { $._spritely.instances[el_id]['remaining_frames'] --; if ($._spritely.instances[el_id]['remaining_frames'] == 0) { $._spritely.instances[el_id]['remaining_frames'] = -1; delete $._spritely.instances[el_id]['remaining_frames']; return; } else { animate(el); } } else if ($._spritely.instances[el_id]['remaining_frames'] != -1) { animate(el); } } else if (options.type == 'pan') { if (!$._spritely.instances[el_id]['_stopped']) { if (options.dir == 'up') { $._spritely.instances[el_id]['l'] = $._spritely.getBgX(el).replace('px', ''); $._spritely.instances[el_id]['t'] = ($._spritely.instances[el_id]['t'] - (options.speed || 1)) || 0; } else if (options.dir == 'down') { $._spritely.instances[el_id]['l'] = $._spritely.getBgX(el).replace('px', ''); $._spritely.instances[el_id]['t'] = ($._spritely.instances[el_id]['t'] + (options.speed || 1)) || 0; } else if (options.dir == 'left') { $._spritely.instances[el_id]['l'] = ($._spritely.instances[el_id]['l'] - (options.speed || 1)) || 0; $._spritely.instances[el_id]['t'] = $._spritely.getBgY(el).replace('px', ''); } else { $._spritely.instances[el_id]['l'] = ($._spritely.instances[el_id]['l'] + (options.speed || 1)) || 0; $._spritely.instances[el_id]['t'] = $._spritely.getBgY(el).replace('px', ''); } // When assembling the background-position string, care must be taken // to ensure correct formatting.. var bg_left = $._spritely.instances[el_id]['l'].toString(); if (bg_left.indexOf('%') == -1) { bg_left += 'px '; } else { bg_left += ' '; } var bg_top = $._spritely.instances[el_id]['t'].toString(); if (bg_top.indexOf('%') == -1) { bg_top += 'px '; } else { bg_top += ' '; } $(el).css('background-position', bg_left + bg_top); } } $._spritely.instances[el_id]['options'] = options; window.setTimeout(function() { $._spritely.animate(options); }, parseInt(1000 / options.fps)); }, randomIntBetween: function(lower, higher) { return parseInt(rand_no = Math.floor((higher - (lower - 1)) * Math.random()) + lower); }, getBgY: function(el) { if ($.browser.msie) { // fixme - the background-position property does not work // correctly in IE so we have to hack it here... Not ideal // especially as $.browser is depricated var bgY = $(el).css('background-position-y') || '0'; } else { var bgY = ($(el).css('background-position') || ' ').split(' ')[1]; } return bgY; }, getBgX: function(el) { if ($.browser.msie) { // see note, above var bgX = $(el).css('background-position-x') || '0'; } else { var bgX = ($(el).css('background-position') || ' ').split(' ')[0]; } return bgX; }, get_rel_pos: function(pos, w) { // return the position of an item relative to a background // image of width given by w var r = pos; if (pos < 0) { while (r < 0) { r += w; } } else { while (r > w) { r -= w; } } return r; } }; $.fn.extend({ spritely: function(options) { var options = $.extend({ type: 'sprite', do_once: false, width: null, height: null, fps: 12, no_of_frames: 2, stop_after: null }, options || {}); var el_id = $(this).attr('id'); if (!$._spritely.instances) { $._spritely.instances = {}; } if (!$._spritely.instances[el_id]) { if (options.start_at_frame) { $._spritely.instances[el_id] = {current_frame: options.start_at_frame - 1}; } else { $._spritely.instances[el_id] = {current_frame: -1}; } } $._spritely.instances[el_id]['type'] = options.type; $._spritely.instances[el_id]['depth'] = options.depth; options.el = this; options.width = options.width || $(this).width() || 100; options.height = options.height || $(this).height() || 100; var get_rate = function() { return parseInt(1000 / options.fps); } if (!options.do_once) { window.setTimeout(function() { $._spritely.animate(options); }, get_rate(options.fps)); } else { $._spritely.animate(options); } return this; // so we can chain events }, sprite: function(options) { var options = $.extend({ type: 'sprite', bounce: [0, 0, 1000] // up-down, left-right, milliseconds }, options || {}); return $(this).spritely(options); }, pan: function(options) { var options = $.extend({ type: 'pan', dir: 'left', continuous: true, speed: 1 // 1 pixel per frame }, options || {}); return $(this).spritely(options); }, flyToTap: function(options) { var options = $.extend({ el_to_move: null, type: 'moveToTap', ms: 1000, // milliseconds do_once: true }, options || {}); if (options.el_to_move) { $(options.el_to_move).active(); } if ($._spritely.activeSprite) { if (window.Touch) { // iphone method see http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone/9 or http://www.nimblekit.com/tutorials.html for clues... $(this)[0].ontouchstart = function(e) { var el_to_move = $._spritely.activeSprite; var touch = e.touches[0]; var t = touch.pageY - (el_to_move.height() / 2); var l = touch.pageX - (el_to_move.width() / 2); el_to_move.animate({ top: t + 'px', left: l + 'px' }, 1000); }; } else { $(this).click(function(e) { var el_to_move = $._spritely.activeSprite; $(el_to_move).stop(true); var w = el_to_move.width(); var h = el_to_move.height(); var l = e.pageX - (w / 2); var t = e.pageY - (h / 2); el_to_move.animate({ top: t + 'px', left: l + 'px' }, 1000); }); } } return this; }, // isDraggable requires jQuery ui isDraggable: function(options) { if ((!$(this).draggable)) { //console.log('To use the isDraggable method you need to load jquery-ui.js'); return this; } var options = $.extend({ type: 'isDraggable', start: null, stop: null, drag: null }, options || {}); var el_id = $(this).attr('id'); if (!$._spritely.instances[el_id]) { return this; } $._spritely.instances[el_id].isDraggableOptions = options; $(this).draggable({ start: function() { var el_id = $(this).attr('id'); $._spritely.instances[el_id].stop_random = true; $(this).stop(true); if ($._spritely.instances[el_id].isDraggableOptions.start) { $._spritely.instances[el_id].isDraggableOptions.start(this); } }, drag: options.drag, stop: function() { var el_id = $(this).attr('id'); $._spritely.instances[el_id].stop_random = false; if ($._spritely.instances[el_id].isDraggableOptions.stop) { $._spritely.instances[el_id].isDraggableOptions.stop(this); } } }); return this; }, active: function() { // the active sprite $._spritely.activeSprite = this; return this; }, activeOnClick: function() { // make this the active script if clicked... var el = $(this); if (window.Touch) { // iphone method see http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone/9 or http://www.nimblekit.com/tutorials.html for clues... el[0].ontouchstart = function(e) { $._spritely.activeSprite = el; }; } else { el.click(function(e) { $._spritely.activeSprite = el; }); } return this; }, spRandom: function(options) { var options = $.extend({ top: 50, left: 50, right: 290, bottom: 320, speed: 4000, pause: 0 }, options || {}); var el_id = $(this).attr('id'); if (!$._spritely.instances[el_id]) { return this; } if (!$._spritely.instances[el_id].stop_random) { var r = $._spritely.randomIntBetween; var t = r(options.top, options.bottom); var l = r(options.left, options.right); $('#' + el_id).animate({ top: t + 'px', left: l + 'px' }, options.speed) } window.setTimeout(function() { $('#' + el_id).spRandom(options); }, options.speed + options.pause) return this; }, makeAbsolute: function() { // remove an element from its current position in the DOM and // position it absolutely, appended to the body tag. return this.each(function() { var el = $(this); var pos = el.position(); el.css({position: "absolute", marginLeft: 0, marginTop: 0, top: pos.top, left: pos.left }) .remove() .appendTo("body"); }); }, spSet: function(prop_name, prop_value) { var el_id = $(this).attr('id'); $._spritely.instances[el_id][prop_name] = prop_value; return this; }, spGet: function(prop_name, prop_value) { var el_id = $(this).attr('id'); return $._spritely.instances[el_id][prop_name]; }, spStop: function(bool) { $(this).each(function() { var el_id = $(this).attr('id'); $._spritely.instances[el_id]['_last_fps'] = $(this).spGet('fps'); $._spritely.instances[el_id]['_stopped'] = true; $._spritely.instances[el_id]['_stopped_f1'] = bool; if ($._spritely.instances[el_id]['type'] == 'sprite') { $(this).spSet('fps', 0); } if (bool) { // set background image position to 0 var bp_top = $._spritely.getBgY($(this)); $(this).css('background-position', '0 ' + bp_top); } }); return this; }, spStart: function() { $(this).each(function() { var el_id = $(this).attr('id'); var fps = $._spritely.instances[el_id]['_last_fps'] || 12; $._spritely.instances[el_id]['_stopped'] = false; if ($._spritely.instances[el_id]['type'] == 'sprite') { $(this).spSet('fps', fps); } }); return this; }, spToggle: function() { var el_id = $(this).attr('id'); var stopped = $._spritely.instances[el_id]['_stopped'] || false; var stopped_f1 = $._spritely.instances[el_id]['_stopped_f1'] || false; if (stopped) { $(this).spStart(); } else { $(this).spStop(stopped_f1); } return this; }, fps: function(fps) { $(this).each(function() { $(this).spSet('fps', fps); }); return this; }, spSpeed: function(speed) { $(this).each(function() { $(this).spSet('speed', speed); }); return this; }, spRelSpeed: function(speed) { $(this).each(function() { var rel_depth = $(this).spGet('depth') / 100; $(this).spSet('speed', speed * rel_depth); }); return this; }, spChangeDir: function(dir) { $(this).each(function() { $(this).spSet('dir', dir); }); return this; }, spState: function(n) { $(this).each(function() { // change state of a sprite, where state is the vertical // position of the background image (e.g. frames row) var yPos = ((n - 1) * $(this).height()) + 'px'; var xPos = $._spritely.getBgX($(this)); var bp = xPos + ' -' + yPos; $(this).css('background-position', bp); }); return this; }, lockTo: function(el, options) { $(this).each(function() { var el_id = $(this).attr('id'); if (!$._spritely.instances[el_id]) { return this; } $._spritely.instances[el_id]['locked_el'] = $(this); $._spritely.instances[el_id]['lock_to'] = $(el); $._spritely.instances[el_id]['lock_to_options'] = options; window.setInterval(function() { if ($._spritely.instances[el_id]['lock_to']) { var locked_el = $._spritely.instances[el_id]['locked_el']; var locked_to_el = $._spritely.instances[el_id]['lock_to']; var locked_to_options = $._spritely.instances[el_id]['lock_to_options']; var locked_to_el_w = locked_to_options.bg_img_width; var locked_to_el_h = locked_to_el.height(); var locked_to_el_y = $._spritely.getBgY(locked_to_el); var locked_to_el_x = $._spritely.getBgX(locked_to_el); var el_l = (parseInt(locked_to_el_x) + parseInt(locked_to_options['left'])); var el_t = (parseInt(locked_to_el_y) + parseInt(locked_to_options['top'])); el_l = $._spritely.get_rel_pos(el_l, locked_to_el_w); $(locked_el).css({ 'top': el_t + 'px', 'left': el_l + 'px' }); } }, options.interval || 20); }); return this; }, destroy: function() { var el = $(this); var el_id = $(this).attr('id'); delete $._spritely.instances[el_id] return this; } }) })(jQuery); // Stop IE6 re-loading background images continuously try { document.execCommand("BackgroundImageCache", false, true); } catch(err) {} ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Model/BlogModel.cs ================================================ namespace Nancy.Demo.MarkdownViewEngine { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using MarkdownSharp; [Serializable] public class BlogModel { private static readonly Regex SSVESubstitution = new Regex("^@[^$]*?$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); private readonly Markdown parser = new Markdown(); private readonly Dictionary metaData = new Dictionary(); public string Title { get; private set; } public string Abstract { get; private set; } public DateTime BlogDate { get; private set; } public string FriendlyDate { get { return BlogDate.ToString("dddd,MMMM dd, yyyy"); } } public IEnumerable Tags { get; private set; } public string Slug { get; private set; } public BlogModel(string markdown) { string metadata = markdown.Contains("@Tags") ? markdown.Substring(markdown.IndexOf("@Tags", StringComparison.Ordinal) + 5, markdown.IndexOf("@EndTags", StringComparison.Ordinal) - 7) : string.Empty; var metadataSplit = metadata.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); foreach (var item in metadataSplit) { var itemdata = item.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries); if (itemdata.Length < 2) continue; metaData.Add(itemdata[0].Trim(), itemdata[1].Trim()); } BlogDate = GetBlogDate(); Title = GetTitle(); Slug = GenerateSlug(); Abstract = GetAbstract(markdown); Tags = GetTags(); } private IEnumerable GetTags() { if (!metaData.Any(x => x.Key == "Tags")) return Enumerable.Empty(); var csv = metaData.FirstOrDefault(x => x.Key == "Tags").Value; return csv.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()); } private DateTime GetBlogDate() { if (!metaData.Any(x => x.Key == "Date")) return DateTime.MinValue; var kvp = metaData.FirstOrDefault(x => x.Key == "Date"); DateTime blogDateTime = DateTime.ParseExact(kvp.Value, "dd/MM/yyyy", CultureInfo.InvariantCulture); return blogDateTime; } private string GetTitle() { if (!metaData.Any(x => x.Key == "Title")) return string.Empty; return metaData.FirstOrDefault(x => x.Key == "Title").Value; } private string GetAbstract(string content) { if (string.IsNullOrWhiteSpace(content)) return string.Empty; if (content.Contains("@Tags")) { content = content.Substring(content.IndexOf("@EndTags", StringComparison.Ordinal) + 8); } string ssveRemoved = SSVESubstitution.Replace(content, "").Trim(); var abstractpost = ssveRemoved.Substring(0, 175).Trim(); var html = parser.Transform(abstractpost); html = html + "

Read More..."; return html.Substring(html.IndexOf("

", StringComparison.Ordinal)); } private string GenerateSlug(char spacer = '-', bool removeStopWords = false) { string str = RemoveAccent(Title).ToLower(); str = str.Replace("-", " "); str = Regex.Replace(str, @"[^a-z0-9\s-]", ""); // invalid chars str = Regex.Replace(str, @"\s+", " ").Trim(); // convert multiple spaces into one space str = str.Substring(0, str.Length <= 100 ? str.Length : 100).Trim(); // cut and trim it str = Regex.Replace(str, @"\s", spacer.ToString(CultureInfo.InvariantCulture)); // hyphens if (removeStopWords) { var stopWords = "a,all,also,am,an,and,as,at,be,but,by,can,could,did,do,does,for,from,get,got,had,has,have,he,how,i,if,in,into,is,it,its,me,my,no,nor,not,of,off,on,or,other,our,so,some,than,that,the,their,them,then,there,these,they,this,tis,to,too,us,was,we,were,yet,you,your".Split(','); str = string.Join(spacer.ToString(CultureInfo.InvariantCulture), str.Split(spacer).Where(o => !stopWords.Contains(o))); } return str; } public string RemoveAccent(string txt) { byte[] bytes = Encoding.GetEncoding("Cyrillic").GetBytes(txt); return Encoding.ASCII.GetString(bytes); } } } ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Modules/HomeModule.cs ================================================ namespace Nancy.Demo.MarkdownViewEngine.Modules { using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using Nancy.ViewEngines; public class HomeModule : NancyModule { private readonly IViewLocationProvider viewLocationProvider; public HomeModule(IViewLocationProvider viewLocationProvider) { this.viewLocationProvider = viewLocationProvider; Get("/", args => { var popularposts = GetModel(); dynamic postModel = new ExpandoObject(); postModel.PopularPosts = popularposts; postModel.MetaData = popularposts; return View["blogindex", postModel]; }); Get("/{viewname}", args => { var popularposts = GetModel(); dynamic postModel = new ExpandoObject(); postModel.PopularPosts = popularposts; postModel.MetaData = popularposts.FirstOrDefault(x => x.Slug == args.viewname); return View["Posts/" + args.viewname, postModel]; }); } private IEnumerable GetModel() { var views = this.viewLocationProvider.GetLocatedViews(new[] { "md", "markdown" }); var model = views.Select(x => { using (var reader = x.Contents()) { var markdown = reader.ReadToEnd(); return new BlogModel(markdown); } }) .Where(x => x.BlogDate.Date <= DateTime.Today) //Allow for future posts to be lined up but don't show .OrderByDescending(x => x.BlogDate) .ToList(); return model; } } } ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Nancy.Demo.MarkdownViewEngine.csproj ================================================  Debug AnyCPU 10.0.0 2.0 {B6929D0B-5104-4C7E-979D-F4914E813FA6} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Nancy.Demo.MarkdownViewEngine Nancy.Demo.MarkdownViewEngine 4.0 false ..\..\ true v4.5 True full False bin DEBUG; prompt 4 False false none True bin prompt 4 False false False ..\..\packages\MarkdownSharp.1.13.0.0\lib\35\MarkdownSharp.dll 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) true bin\ DEBUG; full AnyCPU prompt MinimumRecommendedRules.ruleset false bin\ true AnyCPU prompt MinimumRecommendedRules.ruleset false False True 52805 / False False False {15b7f794-0bb2-4b66-ad78-4a951f1209b2} Nancy.Hosting.Aspnet {864af449-0902-44fc-beee-06ba9a6f4a8f} Nancy.ViewEngines.Markdown {34576216-0dca-4b0f-a0dc-9075e75a676f} Nancy ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/Posts/future-post.md ================================================ @Master['master'] @Tags Date: 21/02/2099 Title: Future Post Tags: Blogging,Internet @EndTags @Section['Content'] @Partial['blogheader', Model.MetaData]; # Future post! Now that we know who you are, I know who I am. I'm not a mistake! It all makes sense! In a comic, you know how you can tell who the arch-villain's going to be? He's the exact opposite of the hero. And most times they're friends, like you and me! I should've known way back when... You know why, David? Because of the kids. They called me Mr Glass. @Partial['blogfooter', Model.MetaData]; @EndSection ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/Posts/my-first-blog-post.md ================================================ @Master['master'] @Tags Date: 21/02/2012 Title: My First Blog Post Tags: Blogging,Internet @EndTags @Section['Content'] @Partial['blogheader', Model.MetaData]; # First post! Bacon ipsum dolor sit amet strip steak frankfurter bacon corned beef tenderloin chuck, prosciutto beef turkey tri-tip. Prosciutto spare ribs strip steak pork loin bresaola, pork chop sausage. Tri-tip salami ham hock pork pork belly fatback bresaola prosciutto turducken sirloin pork loin andouille sausage. Drumstick sausage tri-tip filet mignon doner corned beef kielbasa, chuck swine shank salami. Prosciutto pork chop biltong cow, leberkas ham hock ground round swine sausage ball tip tenderloin. Turducken pork chop ball tip, short loin frankfurter jowl pig. Prosciutto tail tri-tip, pancetta meatloaf doner ham flank venison pork loin filet mignon sausage. @Partial['blogfooter', Model.MetaData]; @EndSection ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/Posts/readme.md ================================================ @Master['master'] @Tags Date: 15/03/2013 Title: Readme Tags: Nancy,Runtime @EndTags @Section['Content'] @Partial['blogheader', Model.MetaData]; # Readme! ## Markdown Viewengine This Markdown Viewengine allows views to be written in Markdown. * Full Model support * Master page support * Supports HTML within any MD content * Simple call `return View["Home"]` for Nancy to render your MD file ## Markdown Blog Demo * Allows for dropping in Markdown files into a directory * Allows for future post scheduling * Generates slug automatically * Uses meta data for date, tags, title * Allows for custom HTML design @Partial['blogfooter', Model.MetaData]; @EndSection ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/Posts/why-use-nancy.md ================================================ @Master['master'] @Tags Date: 19/12/2012 Title: Why use Nancy Tags: Nancy,.Net,C# @EndTags @Section['Content'] @Partial['blogheader', Model.MetaData]; # Why use Nancy? When a new project comes along why should you automatically choose ASP.NET MVC? Yes, its Microsoft based so you may have more of your peers fluent already in that architecture but is there an alternative, a better alternative? I believe so and its called [NancyFX][1]. Your first reaction, what is so special about Nancy? I also believe you’ll ask what is wrong with ASP.NET MVC but maybe you should look at it differently and ask what is right with Nancy? ## What is Nancy? Nancy is a lightweight framework for building websites / services without getting in your way. It’s heavily inspired by a Ruby project called Sinatra, which happens to identify itself as not being a framework, since it doesn’t include all the plumbing of things such as an ORM, lots of configuration, etc. ##Does it implement MVC? Nancy does not force you to adhere to the model-view-controller pattern, or any other pattern. It’s nothing more than a service endpoint responding to HTTP verbs. Making it ideal for building Websites, Web Services and APIs. That doesn’t mean you can’t apply the MVC pattern to Nancy. You can define Views and put them in a Views folder, create Models to return from your endpoints, and map requests to Models, just like you currently do with ASP.NET MVC. ##Key Considerations __Easier Testing__ - Nancy provides a testing library that allows you to test the full request/response cycle so not only can you test that your request returns the model you expect you can test that when you pass in accept headers the response is in the format you expect. For example: [Fact] public void GetData_WhenRequested_ShouldReturnOKStatusCode() { var browser = new Browser(); var response = await browser.Get("/GetData", (with) => { with.Header("Authorization", "Bearer johnsmith"); with.Header("Accept", "application/json"); with.HttpRequest(); }); Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); } I am unaware of how you would be able to test this in MVC without it being a full integration test whereas Nancy has no dependencies on System.Web or MVC so it can provide us with a Response without hitting a server. __Automatic Dependency Resolution__ - Nancy provides an in built IOC container called [TinyIOC][2] which will find all your dependencies automatically for you or if you want/need to configure something you can do so at various points in your application. This is done in a Bootstrapper class that exposes various methods and properties to allow you to configure Nancy. protected override void ConfigureApplicationContainer(TinyIoCContainer container) { base.ConfigureApplicationContainer(container); var store = new EmbeddableDocumentStore() { ConnectionStringName = "RavenDB" }; store.Initialize(); container.Register(store); } protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context) { base.ConfigureRequestContainer(container, context); var store = container.Resolve(); var documentSession = store.OpenSession(); container.Register(documentSession); } Here the IOC container is used in different places within an application's lifecycle. Once at the startup and once per request. It registers a DocumentStore which should be done only once in an application and then on every request it finds the DocumentStore and uses it to open a session and registers it with the IOC. If you have a service that has a IDocumentSession dependency then it will come via this. If for some reason you're being stubborn and want to use your preferred IOC container, Nancy supports all the main IOC players allowing you to register your dependencies with them instead. __Completely Customisable, Conventions & Better Extension Points__ - One of Nancy's core features is its extensibility. It it designed to allow you to replace any part you want. You can have custom model binders, view renderers, serializers in fact you can implement your own INancyEngine and completely change how Nancy handles requests etc. There are also a set of pre-defined conventions that you can swap in/out if you want Nancy to do something different than what comes as standard. Everything is complete customisable and very easy to modify Nancy's behaviour which offers great extensibility points if you wanted to create a 3rd party library for example. __Terse Syntax & Less Ceremony__ - Nancy provides a nice terse syntax that does not get in the way of your application and leaves you to write your code. What I have found is that due to the terse syntax it encourages you to make your application code nice and neat too. One example of less ceremony and terseness is that you can get a full Nancy application running inside a 140 character tweet! public class HelloModule : NancyModule { public HelloModule() { Get["/"] = parameters => "Hello World"; } } __Runs on Mono__ - Nancy does not tie itself down to Windows it works just as well on OSX and Linux under [Mono][3] which allows your team to work on multiple platforms. In fact Nancy can even run on a [Raspberry Pi][4] I would like to see ASP.NET MVC do that! __Content Negotiation__ - Content Negotiation is built into Nancy and runs out of the box. This means Nancy can be used in an API type application as well as a website application. In fact if you wanted you could have it do both very easily. Get["/"] = parameters => { return Negotiate .WithModel(new RatPack {FirstName = "Nancy "}) .WithMediaRangeModel("text/html", new RatPack {FirstName = "Nancy fancy pants"}) .WithView("negotiatedview") .WithHeader("X-Custom", "SomeValue"); }; This demo highlights that if you made a request to "/" in your application by a web browser it will return a specific model with a property name of "Nancy fancy pants", return a view called "negotiatedview" and return a custom header. However, if your API client made a request to "/" it would return a model with "Nancy" and a custom header. The resulting model would then be serialized into JSON, JSONP, XML or any other variation specified in the Accept header from your client. This example is possibly contrived somewhat but Nancy supplies conneg from all routes so something like the below would be serialized based on the headers. Get["/"] = parameters => { var model = MyModel(); return model; }; __No Config__ - To get Nancy up and running there is no config required, no nasty XML files to modify, nothing. As its host agnostic you don't have to modify anything in web.config to have it running via IIS for example. __Runs Anywhere__ - As I just mentioned Nancy is host agnostic which means you can run it in IIS, WCF, embedded within a EXE, as a windows service or within a self hosted application. Pretty much everywhere! __Pipeline Hooks__ - Nancy allows you to modify the pipeline ie.the request and response before and after they are invoked. One simple example is saving your data at the end of a request. protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) { base.RequestStartup(container, pipelines, context); pipelines.AfterRequest.AddItemToEndOfPipeline( (ctx) => { var documentSession = container.Resolve(); if (ctx.Response.StatusCode != HttpStatusCode.InternalServerError) { documentSession.SaveChanges(); } documentSession.Dispose(); }); } Here we configure the AfterRequest delegate to find the IDocumentSession used in the request, save the changes to the database and then dispose of the IDocumentSession (although TinyIOC would actually dispose of this for you). A more complex example could be that you modify the way the Request.Form is populated on a HTTP POST, it is that extensible and configurable you could do that quite easily. __No ties to System.Web and a Freely Designed Framework__ - System.Web is the core DLL based in ASP.Net. It contains the whole kitchen sink of the framework so you get everything bundled into your application even if you only use 25% of the possibilities. Nancy is architected the other way in that there are [numerous plugins][5] that supply additional and alternative functionality. Nancy is also not bound to any specific implementation or framework and all requests and responses are built from the ground up allowing it to be loosely coupled and free. This also means that Nancy can run in the .Net client profile environments without the added requirement for .Net full profile that ASP.NET MVC does require. __Support & Community__ - One of the great things about Nancy is its community and support. They have a very active [Google group][6] and you'll find loads of help in [Jabbr][7] to get your questions answered ASAP. There is a real feeling of community and support because people want to spread the good word about Nancy. It has over 100 contributors to the project but keep in mind the vision, impetus and most of the work is done by 2 guys, [@TheCodeJunkie][8] and [@GrumpyDev][9] not a huge team sitting in Redmond. One final thing the [swag][10] is a lot more stylish than Microsoft t-shirts :) ##Conclusion In one of my last [blog posts][11] I described how you could test the full pipeline in ASP.NET Web API because Microsoft don't supply a nice way to do it. This blog post got the attention of Microsoft and at the time of writing this blog post it has had over 3150 hits and appeared on the home page of ASP.NET. The core of the code in that post was taken out of Nancy. So please if you liked what you saw there, give Nancy a try I think you'll find there many benefits described above as well as others I've not mentioned. It is your duty as software developers to try new things and investigate tools. So when your next project comes about and your manager says "Ok lets write our new app in ASP.NET MVC" your reactions should be reflected by these animated GIFs. __Should we really use ASP.NET MVC?__ ![Should we use ASP.NET MVC?](http://i.minus.com/imM6lHNgto38I.gif) __The boss says we can use Nancy!__ ![All systems go](http://i.minus.com/i3nPmdfBC2VaH.gif) __We have our new Nancy app up and running in no time!__ ![Up and Running](http://i.minus.com/ie4Om0y5NXhS1.gif) For more infomation on Nancy checkout the [website][12] and [documentation][13]. [1]: http://nancyfx.org/ [2]: https://github.com/grumpydev/TinyIoC [3]: http://www.mono-project.com/Main_Page [4]: http://www.raspberrypi.org/quick-start-guide [5]: http://nuget.org/packages?q=nancy [6]: https://groups.google.com/forum/?fromgroups#!forum/nancy-web-framework [7]: http://jabbr.net/#/rooms/nancyfx [8]: http://twitter.com/TheCodeJunkie [9]: http://twitter.com/GrumpyDev [10]: http://nancyfx.spreadshirt.net [11]: http://blog.jonathanchannon.com/2012/11/29/asp-net-web-api-testing [12]: http://nancyfx.org/ [13]: https://github.com/NancyFx/Nancy/wiki/Documentation @Partial['blogfooter', Model.MetaData]; @EndSection ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/blogfooter.html ================================================ 

================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/blogheader.html ================================================ 
@Model.FriendlyDate

================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/blogindex.html ================================================ @Master['master'] @Section['Content'] @Each.MetaData @EndEach @EndSection ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/master.html ================================================  Nancy.Demo.MarkdownViewEngine
 
 
 
Copyright © Legit
Design by Dzignine
@Section['Content']
 
================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/Views/popularposts.html ================================================ 

Popular Posts

================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/packages.config ================================================  ================================================ FILE: samples/Nancy.Demo.MarkdownViewEngine/web.config ================================================ ================================================ FILE: samples/Nancy.Demo.ModelBinding/CustomersModule.cs ================================================ namespace Nancy.BindingDemo { using System.Linq; using Nancy.Demo.ModelBinding.Database; using Nancy.Demo.ModelBinding.Models; using Nancy.ModelBinding; public class CustomersModule : NancyModule { public CustomersModule() : base("/customers") { Get("/", args => { var model = DB.Customers.OrderBy(e => e.RenewalDate).ToArray(); return View["Customers", model]; }); Post("/", args => { Customer model = this.Bind(); var model2 = this.Bind(); DB.Customers.Add(model); DB.Customers.Add(model2); return this.Response.AsRedirect("/Customers"); }); } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/Database/DB.cs ================================================ namespace Nancy.Demo.ModelBinding.Database { using System.Collections.Generic; using Nancy.Demo.ModelBinding.Models; public static class DB { public static List Events { get; private set; } public static List Customers { get; private set; } static DB() { Events = new List(); Customers = new List(); } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/EventsModule.cs ================================================ namespace Nancy.Demo.ModelBinding { using System.Linq; using Nancy.Demo.ModelBinding.Database; using Nancy.Demo.ModelBinding.Models; using Nancy.ModelBinding; public class EventsModule : NancyModule { public EventsModule() : base("/events") { Get("/", args => { var model = DB.Events.OrderBy(e => e.Time).ToArray(); return View["Events", model]; }); Post("/", args => { Event model = this.Bind(); var model2 = this.Bind("Location"); // Blacklist location DB.Events.Add(model); DB.Events.Add(model2); return this.Response.AsRedirect("/Events"); }); } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/JsonModule.cs ================================================ namespace Nancy.Demo.ModelBinding { using System.Text; using Nancy.Demo.ModelBinding.Models; using Nancy.ModelBinding; public class JsonModule : NancyModule { public JsonModule() { Get("/bindjson", args => { return View["PostJson"]; }); Post("/bindjson", args => { User model = this.Bind(); var sb = new StringBuilder(); sb.AppendLine("Bound Model:"); sb.Append("Type: "); sb.AppendLine(model.GetType().FullName); sb.Append("Name: "); sb.AppendLine(model.Name); sb.Append("Address: "); sb.AppendLine(model.Address); return sb.ToString(); }); } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/MainModule.cs ================================================ namespace Nancy.Demo.ModelBinding { public class MainModule : NancyModule { public MainModule() { Get("/", args => { return "Events (default model binder)
Customers (custom model binder)
Users (JSON)
Users (XML)
"; }); } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/ModelBinders/CustomerModelBinder.cs ================================================ namespace Nancy.Demo.ModelBinding.ModelBinders { using System; using Nancy.Demo.ModelBinding.Models; using Nancy.ModelBinding; /// /// Sample model binder that manually binds customer models /// public class CustomerModelBinder : IModelBinder { /// /// Whether the binder can bind to the given model type /// /// Required model type /// True if binding is possible, false otherwise public bool CanBind(Type modelType) { return modelType == typeof(Customer); } /// /// Bind to the given model type /// /// Current context /// Model type to bind to /// Optional existing instance /// The that should be applied during binding. /// Blacklisted property names /// Bound model public object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList) { var customer = (instance as Customer) ?? new Customer(); customer.Name = customer.Name ?? context.Request.Form["Name"]; customer.RenewalDate = customer.RenewalDate == default(DateTime) ? context.Request.Form["RenewalDate"] : customer.RenewalDate; return customer; } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/ModelBindingBootstrapper.cs ================================================ namespace Nancy.Demo.ModelBinding { public class ModelBindingBootstrapper : DefaultNancyBootstrapper { } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/Models/Customer.cs ================================================ namespace Nancy.Demo.ModelBinding.Models { using System; public class Customer { public int Id { get; set; } public string Name { get; set; } public DateTime RenewalDate { get; set; } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/Models/Event.cs ================================================ namespace Nancy.Demo.ModelBinding.Models { using System; using System.Collections.Generic; public class Event { public int Id { get; set; } public string Title { get; set; } public string Location { get; set; } public IEnumerable Venues { get; set; } public DateTime Time { get; set; } public Event() { this.Title = "Default"; this.Location = "Default"; this.Time = DateTime.Now; } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/Models/User.cs ================================================ namespace Nancy.Demo.ModelBinding.Models { public class User { public string Name { get; set; } public string Address { get; set; } } } ================================================ FILE: samples/Nancy.Demo.ModelBinding/Nancy.Demo.ModelBinding.csproj ================================================  Debug AnyCPU 2.0 {1258BFCD-3BAD-4373-B786-4D698EC3C157} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.ModelBinding Nancy.Demo.ModelBinding v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.ModelBinding.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.ModelBinding.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 1953 / False False False ================================================ FILE: samples/Nancy.Demo.ModelBinding/Views/Customers.spark ================================================  Customers

Customers

Customer is added twice, one using the dynamic binder adapter, the other using the generic one.

Current customers:

Add another

================================================ FILE: samples/Nancy.Demo.ModelBinding/Views/Events.spark ================================================  Events

Events

Event is added twice, one using the dynamic binder adapter, the other using the generic one.

The second one has 'Location' marked as blacklisted so should appear as 'Default'

Current events:

Add another

Venue 1 Venue 2 Venue 3 Venue 4
================================================ FILE: samples/Nancy.Demo.ModelBinding/Views/PostJson.html ================================================ JSON Post Test JSON Post ================================================ FILE: samples/Nancy.Demo.ModelBinding/Views/PostXml.html ================================================ XML Post Test XML Post ================================================ FILE: samples/Nancy.Demo.ModelBinding/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.ModelBinding/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.ModelBinding/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.ModelBinding/XmlModule.cs ================================================ namespace Nancy.Demo.ModelBinding { using System.Text; using Nancy.Demo.ModelBinding.Models; using Nancy.ModelBinding; public class XmlModule : NancyModule { public XmlModule() { Get("/bindxml", args => { return View["PostXml"]; }); Post("/bindxml", args => { var model = this.Bind(u => u.Name); var sb = new StringBuilder(); sb.AppendLine("Bound Model:"); sb.Append("Type: "); sb.AppendLine(model.GetType().FullName); sb.Append("Name: (which will be empty because it's ignored)"); sb.AppendLine(model.Name); sb.Append("Address: "); sb.AppendLine(model.Address); return sb.ToString(); }); } } } ================================================ FILE: samples/Nancy.Demo.Razor.Localization/CustomResourceAssemblyProvider.cs ================================================ namespace Nancy.Demo.Razor.Localization { using System.Collections.Generic; using System.Reflection; /// /// Use a custom because the default one ignores any /// assembly that starts with Nancy*. For normal applications this is not required to /// implement. /// public class CustomResourceAssemblyProvider : IResourceAssemblyProvider { private readonly IAssemblyCatalog assemblyCatalog; private IEnumerable filteredAssemblies; public CustomResourceAssemblyProvider(IAssemblyCatalog assemblyCatalog) { this.assemblyCatalog = assemblyCatalog; } public IEnumerable GetAssembliesToScan() { return (this.filteredAssemblies ?? (this.filteredAssemblies = this.assemblyCatalog.GetAssemblies())); } } } ================================================ FILE: samples/Nancy.Demo.Razor.Localization/DemoBootstrapper.cs ================================================ namespace Nancy.Demo.Razor.Localization { using System; using Nancy.Bootstrapper; public class DemoBootstrapper : DefaultNancyBootstrapper { protected override Func InternalConfiguration { get { return NancyInternalConfiguration.WithOverrides(x => x.ResourceAssemblyProvider = typeof(CustomResourceAssemblyProvider)); } } } } ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Modules/HomeModule.cs ================================================ namespace Nancy.Demo.Razor.Localization.Modules { using System.Globalization; public class HomeModule : NancyModule { public HomeModule() { Get("/", args => View["Index"]); Get("/cultureview", args => View["CultureView"]); Get("/cultureviewgerman", args => { this.Context.Culture = new CultureInfo("de-DE"); return View["CultureView"]; }); } } } ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Nancy.Demo.Razor.Localization.csproj ================================================  true bin\ DEBUG;TRACE full AnyCPU prompt false false bin\ TRACE true pdbonly AnyCPU prompt true true false Debug AnyCPU 2.0 {396F0BCE-5B51-4B6A-931E-312880C24725} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Nancy.Demo.Razor.Localization Nancy.Demo.Razor.Localization v4.5 true true full false bin\ DEBUG;TRACE prompt 4 false pdbonly true bin\ TRACE prompt 4 false True True Text.resx Web.config Web.config PublicResXFileCodeGenerator Text.Designer.cs {15b7f794-0bb2-4b66-ad78-4a951f1209b2} Nancy.Hosting.Aspnet {2c6f51df-015c-4db6-b44c-0e5e4f25e2a9} Nancy.ViewEngines.Razor {34576216-0dca-4b0f-a0dc-9075e75a676f} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 56029 / http://localhost:61703/ False False False ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Resources/Text.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Nancy.Demo.Razor.Localization.Resources { using System.CodeDom.Compiler; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Resources; using System.Runtime.CompilerServices; /// /// 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. [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [DebuggerNonUserCode()] [CompilerGenerated()] public class Text { private static ResourceManager resourceMan; private static CultureInfo resourceCulture; [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Text() { } /// /// Returns the cached ResourceManager instance used by this class. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public static ResourceManager ResourceManager { get { if (ReferenceEquals(resourceMan, null)) { ResourceManager temp = new ResourceManager("Nancy.Demo.Razor.Localization.Resources.Text", typeof(Text).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public static CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Hello Sir. /// public static string Greeting { get { return ResourceManager.GetString("Greeting", resourceCulture); } } } } ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Resources/Text.de-DE.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 Guten Tag ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Resources/Text.en-US.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 Howdy Partner ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Resources/Text.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 Hello Sir ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Views/CultureView-de-DE.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @{ Layout = "razor-layout.cshtml"; }

You're here based on the German culture set in HomeModule however the HomeModule only calls return View["CultureView"]. It uses View Location Conventions therefore there must be a file called CultureView-de-DE.cshtml

================================================ FILE: samples/Nancy.Demo.Razor.Localization/Views/CultureView.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @{ Layout = "razor-layout.cshtml"; }

You're here based on your default culture which is @Html.CurrentLocale

================================================ FILE: samples/Nancy.Demo.Razor.Localization/Views/Index.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @{ Layout = "razor-layout.cshtml"; }

The below greeting should be based on your culture:

"@Text.Text.Greeting"

You will see:
{ "en-GB", "Hello Sir" }
{ "de-DE", "Guten Tag" }
{ "en-US", "Howdy Partner" }

================================================ FILE: samples/Nancy.Demo.Razor.Localization/Views/razor-layout.cshtml ================================================ @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase Razor Localization Demo ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Web.Debug.config ================================================ ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Web.Release.config ================================================ ================================================ FILE: samples/Nancy.Demo.Razor.Localization/Web.config ================================================ 
================================================ FILE: samples/Nancy.Demo.SparkViewEngine/FifthElement/Fifth.spark ================================================  Is she hot or not

This is the fifth element maaaaan! Like whoaaa...

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/FifthElement/FifthElementModule.cs ================================================ namespace Nancy.Demo.SparkViewEngine.FifthElement { public class FifthElementModule : NancyModule { /// /// Initializes a new instance of the class. /// public FifthElementModule() { Get("/5", args => { return View["Fifth.spark"]; }); } } } ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/MainModule.cs ================================================ namespace Nancy.Demo.SparkViewEngine { public class MainModule : NancyModule { /// /// Initializes a new instance of the class. /// public MainModule() { Get("/", args => { return View["Index.spark"]; }); Get("/test", args => View["test"]); Get("/test2", args => View["test2"]); } } } ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Nancy.Demo.SparkViewEngine.csproj ================================================  Debug AnyCPU 2.0 {DE91D085-70DF-4892-B0E3-B849E9BF6AF5} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.SparkViewEngine Nancy.Demo.SparkViewEngine v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.SparkViewEngine.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.SparkViewEngine.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 6459 / False False False ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/Index.spark ================================================  Spark View Engine Demo

This is a sample Spark view!

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/Main/test.spark ================================================ 

This is the content!

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/Main/test2.spark ================================================ 

This is the content for the page which doesn't explicitly define its own master template.

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/Shared/application.spark ================================================  HTML 5 Template

... It's using the application.spark template!

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/Shared/html5.spark ================================================  HTML 5 Template ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Views/_SmallBit.spark ================================================ 

This is a small bit

================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.SparkViewEngine/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/MainModule.cs ================================================ namespace Nancy.Demo.SuperSimpleViewEngine { using Nancy.Demo.SuperSimpleViewEngine.Models; public class MainModule : NancyModule { /// /// Initializes a new instance of the class. /// public MainModule() { Get("/", args => { ViewBag.Test = "Test ViewBag"; var model = new MainModel( "Jimbo", new[] {new User("Bob", "Smith"), new User("Jimbo", "Jones"), new User("Bill", "Bobs"),}, ""); return View["Index", model]; }); } } } ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Models/MainModel.cs ================================================ namespace Nancy.Demo.SuperSimpleViewEngine.Models { using System.Collections.Generic; public class MainModel { public string Name { get; set; } public IEnumerable Users { get; set; } public string NaughtyStuff { get; set; } /// /// Initializes a new instance of the class. /// public MainModel(string name, IEnumerable users, string naughtyStuff) { this.Name = name; this.Users = users; this.NaughtyStuff = naughtyStuff; } } public class User { public string FirstName { get; private set; } public string LastName { get; private set; } /// /// Initializes a new instance of the class. /// public User(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } } } ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Nancy.Demo.SuperSimpleViewEngine.csproj ================================================  Debug AnyCPU 2.0 {4885B9DF-D99C-4A1C-8606-259D32A58C33} {349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Library Properties Nancy.Demo.SuperSimpleViewEngine Nancy.Demo.SuperSimpleViewEngine v4.5 true 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.SuperSimpleViewEngine.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.SuperSimpleViewEngine.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false 4 false Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 57861 / http://localhost:57861/ False False False ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Views/Index.sshtml ================================================ @Master['MasterPage'] @Section['Content']

ViewBag

@ViewBag.Test

This content from the index page

Partials

Login box below rendered via a partial view with no model.

@Partial['login'];

Box below is rendered via a partial with a sub-model passed in.

The submodel is a list which the partial iterates over with Each

@Partial['user', Model.Users];

Encoding

Model output can also be encoded:

@!Model.NaughtyStuff

The context stuff

The requested path: @Context.Request.Path

@EndSection ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Views/Login.sshtml ================================================ 
Username:
Password:
================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Views/MasterPage.sshtml ================================================  Super Simple View Engine

Super Simple View Engine

This text is in the master page, it has access to the model:

Hello @Model.Name!

@Section['Content'] ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Views/User.sshtml ================================================ @Each First name: @Current.FirstName
Surname: @Current.LastName

@EndEach ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.SuperSimpleViewEngine/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Validation/CustomersModule.cs ================================================ namespace Nancy.Demo.Validation { using System.Linq; using Nancy.Demo.Validation.Database; using Nancy.Demo.Validation.Models; using Nancy.ModelBinding; using Nancy.Validation; public class CustomersModule : NancyModule { public CustomersModule() : base("/customers") { Get("/", args => { var model = DB.Customers.OrderBy(e => e.RenewalDate).ToArray(); return View["Customers", model]; }); Get("/poke", args => { var validator = this.ValidatorLocator.GetValidatorForType(typeof(Customer)); return this.Response.AsJson(validator.Description); }); Post("/", args => { Customer model = this.Bind(); var result = this.Validate(model); if (!result.IsValid) { return View["CustomerError", result]; } DB.Customers.Add(model); return this.Response.AsRedirect("/Customers"); }); } } } ================================================ FILE: samples/Nancy.Demo.Validation/Database/DB.cs ================================================ namespace Nancy.Demo.Validation.Database { using System.Collections.Generic; using Nancy.Demo.Validation.Models; public static class DB { public static List Customers { get; private set; } static DB() { Customers = new List(); } } } ================================================ FILE: samples/Nancy.Demo.Validation/MainModule.cs ================================================ namespace Nancy.Demo.Validation { public class MainModule : NancyModule { public MainModule() { Get("/", args => { return "Customers
Products"; }); } } } ================================================ FILE: samples/Nancy.Demo.Validation/Models/Customer.cs ================================================ namespace Nancy.Demo.Validation.Models { using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; public class Customer : IValidatableObject { public int Id { get; set; } [Required, OddLengthString] public string Name { get; set; } [Range(typeof(DateTime), "1/1/2000", "1/1/3000", ErrorMessage = "Value for {0} must be between {1} and {2}")] public DateTime RenewalDate { get; set; } public IEnumerable Validate(ValidationContext validationContext) { if (this.Id > 100) { yield return new ValidationResult("The Id cannot be greater than 100", new[] { "Id" }); } } } } ================================================ FILE: samples/Nancy.Demo.Validation/Models/OddLengthStringAttribute.cs ================================================ namespace Nancy.Demo.Validation.Models { using System.ComponentModel.DataAnnotations; public class OddLengthStringAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { if (value != null && ((string)value).Length % 2 != 0) { return ValidationResult.Success; } return new ValidationResult( this.ErrorMessage = string.Format("The value of {0} was not an odd length", validationContext.MemberName), new[] { validationContext.MemberName } ); } } } ================================================ FILE: samples/Nancy.Demo.Validation/Models/OddLengthStringAttributeAdapter.cs ================================================ namespace Nancy.Demo.Validation.Models { using System.ComponentModel.DataAnnotations; using Nancy.Validation.DataAnnotations; public class OddLengthStringAttributeAdapter : DataAnnotationsValidatorAdapter { public OddLengthStringAttributeAdapter() : base("Compare") { } public override bool CanHandle(ValidationAttribute attribute) { return attribute.GetType() == typeof(OddLengthStringAttribute); } } } ================================================ FILE: samples/Nancy.Demo.Validation/Models/Product.cs ================================================ namespace Nancy.Demo.Validation.Models { using System; using System.Collections.Generic; using FluentValidation; using FluentValidation.Internal; using FluentValidation.Validators; using Nancy.Validation; using Nancy.Validation.FluentValidation; public class Product { public string Name { get; set; } public int Price { get; set; } } public class ProductValidator : AbstractValidator { public ProductValidator() { RuleFor(product => product.Name).NotEmpty(); RuleFor(product => product.Name).Length(1, 10).OddLength(); RuleFor(product => product.Name).Matches("[A-Z]*"); RuleFor(product => product.Name).EmailAddress(); RuleFor(product => product.Price).ExclusiveBetween(10, 15); RuleFor(product => product.Price).InclusiveBetween(10, 15); RuleFor(product => product.Price).Equal(5); } } public class OddLengthStringValidator : PropertyValidator { public OddLengthStringValidator() : base("'{PropertyName}' has to be of odd length.") { } protected override bool IsValid(PropertyValidatorContext context) { var value = context.PropertyValue as string; if (value == null) { return false; } return (value.Length % 2 != 0); } } public static class FluentValidationExtensions { public static IRuleBuilderOptions OddLength(this IRuleBuilder ruleBuilder) { return ruleBuilder.SetValidator(new OddLengthStringValidator()); } } public class OddLengthStringValidatorAdapter : AdapterBase { public override bool CanHandle(IPropertyValidator validator) { return validator is OddLengthStringValidator; } public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new OddLengthStringRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule)); } } public class OddLengthStringRule : ModelValidationRule { public OddLengthStringRule(Func errorMessageFormatter, IEnumerable memberNames) : base("OddLengthString", errorMessageFormatter, memberNames) { } } } ================================================ FILE: samples/Nancy.Demo.Validation/Nancy.Demo.Validation.csproj ================================================  Debug AnyCPU 2.0 {F417B81C-B914-4BFD-8299-6BBD11072B01} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties Nancy.Demo.Validation Nancy.Demo.Validation v4.5 false 4.0 true full false bin\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\ TRACE prompt 4 AllRules.ruleset false true bin\ DEBUG;TRACE full AnyCPU bin\Nancy.Demo.Validation.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false false bin\ TRACE true pdbonly AnyCPU bin\Nancy.Demo.Validation.dll.CodeAnalysisLog.xml true GlobalSuppressions.cs prompt AllRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets false ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false ..\..\packages\FluentValidation.6.3.4-alpha\lib\Net45\FluentValidation.dll True Web.config Web.config Properties\SharedAssemblyInfo.cs {15B7F794-0BB2-4B66-AD78-4A951F1209B2} Nancy.Hosting.Aspnet {701765DC-F4F2-4943-A37F-6ED253198158} Nancy.Validation.DataAnnotations {01230F2A-D108-480C-B834-8CE0569FD3B3} Nancy.Validation.FluentValidation {4B7E35DF-1569-4346-B180-A09615723095} Nancy.ViewEngines.Spark {34576216-0DCA-4B0F-A0DC-9075E75A676F} Nancy Designer 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) False True 3382 / False False False ================================================ FILE: samples/Nancy.Demo.Validation/ProductsModule.cs ================================================ namespace Nancy.Demo.Validation { using Nancy.Demo.Validation.Models; using Nancy.ModelBinding; using Nancy.Validation; public class ProductsModule : NancyModule { public ProductsModule() : base("/products") { Get("/", args => { return "Products module"; }); Get("/poke", args => { var validator = this.ValidatorLocator.GetValidatorForType(typeof(Product)); return this.Response.AsJson(validator.Description); }); Post("/", args => { Product model = this.Bind(); var result = this.Validate(model); if (!result.IsValid) { return View["CustomerError", result]; } return 200; }); } } } ================================================ FILE: samples/Nancy.Demo.Validation/ValidationBootstrapper.cs ================================================ namespace Nancy.Demo.Validation { using Nancy.TinyIoc; public class ValidationBootstrapper : DefaultNancyBootstrapper { protected override void ConfigureApplicationContainer(TinyIoCContainer container) { // Disable auto-registration so that we can make sure that the // application registrations are preformed correctly by each of // the validation projects. This is for testing purposes only // and is not required to perform in your own project. //base.ConfigureApplicationContainer(container); } } } ================================================ FILE: samples/Nancy.Demo.Validation/Views/CustomerError.spark ================================================  Customers

Error submitting customer.

${k}

  • ${e}
================================================ FILE: samples/Nancy.Demo.Validation/Views/Customers.spark ================================================  Customers

Customers

Customer is added twice, one using the dynamic binder adapter, the other using the generic one.

Current customers:

Add another

================================================ FILE: samples/Nancy.Demo.Validation/Web.Debug.config ================================================  ================================================ FILE: samples/Nancy.Demo.Validation/Web.Release.config ================================================  ================================================ FILE: samples/Nancy.Demo.Validation/Web.config ================================================  ================================================ FILE: samples/Nancy.Demo.Validation/packages.config ================================================  ================================================ FILE: src/Directory.Build.props ================================================  Andreas Håkansson, Steven Robbins and contributors ..\..\Nancy.ruleset Debug;Release true true http://nancyfx.org/nancy-nuget.png https://github.com/NancyFx/Nancy/blob/master/license.txt http://nancyfx.org Nancy AnyCPU 2.0.0-clinteastwood All $(DefineConstants);CORE ================================================ FILE: src/Nancy/AfterPipeline.cs ================================================ namespace Nancy { using System; using System.Threading; using System.Threading.Tasks; using Nancy.Helpers; /// /// Intercepts the request after the appropriate route handler has completed its operation. /// The After hooks does not have any return value because one has already been produced by the appropriate route. /// Instead you get the option to modify (or completely replace) the existing response by accessing the Response property of the NancyContext that is passed in. /// /// public class AfterPipeline : AsyncNamedPipelineBase, Action> { /// /// Initializes a new instance of the class. /// public AfterPipeline() { } /// /// Initializes a new instance of the class, with /// the provided . /// /// Number of delegates in pipeline public AfterPipeline(int capacity) : base(capacity) { } /// /// Performs an implicit conversion from to . /// /// The instance. /// The result of the conversion. public static implicit operator Func(AfterPipeline pipeline) { return pipeline.Invoke; } /// /// Performs an implicit conversion from to . /// /// The function. /// /// A new instance with /// public static implicit operator AfterPipeline(Func func) { var pipeline = new AfterPipeline(); pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends a new func to the AfterPipeline /// /// The pipeline. /// The function. /// /// /// public static AfterPipeline operator +(AfterPipeline pipeline, Func func) { pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends a new action to the AfterPipeline /// /// The instance. /// The action. /// /// /// public static AfterPipeline operator +(AfterPipeline pipeline, Action action) { pipeline.AddItemToEndOfPipeline(action); return pipeline; } /// /// Appends the items of an to the other. /// /// The to add to. /// The to add. /// /// /// public static AfterPipeline operator +(AfterPipeline pipelineToAddTo, AfterPipeline pipelineToAdd) { foreach (var pipelineItem in pipelineToAdd.PipelineItems) { pipelineToAddTo.AddItemToEndOfPipeline(pipelineItem); } return pipelineToAddTo; } /// /// Invokes the pipeline items in Nancy context. /// /// The instance. /// The instance public async Task Invoke(NancyContext context, CancellationToken cancellationToken) { foreach (var pipelineDelegate in this.PipelineDelegates) { await pipelineDelegate.Invoke(context, cancellationToken).ConfigureAwait(false); } } /// /// Wraps the specified instance into its async form. /// /// The instance. /// Async instance protected override PipelineItem> Wrap(PipelineItem> pipelineItem) { return new PipelineItem>(pipelineItem.Name, (ctx, ct) => { pipelineItem.Delegate(ctx); return TaskHelpers.CompletedTask; }); } } } ================================================ FILE: src/Nancy/AppDomainAssemblyCatalog.cs ================================================ #if !CORE namespace Nancy { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Nancy.Extensions; using Nancy.Helpers; /// /// Default implementation of the interface, based on /// retrieving information from . /// public class AppDomainAssemblyCatalog : IAssemblyCatalog { private static readonly AssemblyName NancyAssemblyName = typeof(INancyEngine).GetTypeInfo().Assembly.GetName(); private readonly Lazy> assemblies = new Lazy>(GetAvailableAssemblies); /// /// Gets all instances in the catalog. /// /// An of instances. public virtual IReadOnlyCollection GetAssemblies() { return this.assemblies.Value; } private static IReadOnlyCollection GetAvailableAssemblies() { var assemblies = GetLoadedNancyReferencingAssemblies(); var loaded = LoadNancyReferencingAssemblies(assemblies); return assemblies.Union(loaded).ToArray(); } private static List GetLoadedNancyReferencingAssemblies() { var assemblies = new List(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (!assembly.IsDynamic && !assembly.ReflectionOnly && assembly.IsReferencing(NancyAssemblyName)) { assemblies.Add(assembly); } } return assemblies; } private static IEnumerable LoadNancyReferencingAssemblies(IEnumerable loadedAssemblies) { var assemblies = new HashSet(); var inspectionAppDomain = CreateInspectionAppDomain(); var inspectionProber = CreateRemoteReferenceProber(inspectionAppDomain); var loadedNancyReferencingAssemblyNames = loadedAssemblies.Select(assembly => assembly.GetName()).ToArray(); foreach (var directory in GetAssemblyDirectories()) { foreach (var assemblyPath in Directory.EnumerateFiles(directory, "*.dll")) { var unloadedAssemblyName = SafeGetAssemblyName(assemblyPath); if (unloadedAssemblyName == null) { continue; } if (!loadedNancyReferencingAssemblyNames.Any(loadedNancyReferencingAssemblyName => AssemblyName.ReferenceMatchesDefinition(loadedNancyReferencingAssemblyName, unloadedAssemblyName))) { if (inspectionProber.HasReference(unloadedAssemblyName, NancyAssemblyName)) { var assembly = SafeLoadAssembly(AppDomain.CurrentDomain, unloadedAssemblyName); if (assembly != null) { assemblies.Add(assembly); } } } } } AppDomain.Unload(inspectionAppDomain); return assemblies.ToArray(); } private static AppDomain CreateInspectionAppDomain() { var currentAppDomain = AppDomain.CurrentDomain; return AppDomain.CreateDomain("AppDomainAssemblyCatalog", currentAppDomain.Evidence, currentAppDomain.SetupInformation); } private static ProxyNancyReferenceProber CreateRemoteReferenceProber(AppDomain appDomain) { return (ProxyNancyReferenceProber)appDomain.CreateInstanceAndUnwrap( typeof(ProxyNancyReferenceProber).Assembly.FullName, typeof(ProxyNancyReferenceProber).FullName); } private static IEnumerable GetAssemblyDirectories() { var directories = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath != null ? AppDomain.CurrentDomain.SetupInformation.PrivateBinPath.Split(new [] { ';' }, StringSplitOptions.RemoveEmptyEntries) : new string[] { }; foreach (var directory in directories.Where(directory => !string.IsNullOrWhiteSpace(directory))) { yield return directory; } if (AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe == null) { yield return AppDomain.CurrentDomain.SetupInformation.ApplicationBase; } } private static AssemblyName SafeGetAssemblyName(string assemblyPath) { try { return AssemblyName.GetAssemblyName(assemblyPath); } catch { return null; } } private static Assembly SafeLoadAssembly(AppDomain domain, AssemblyName assemblyName) { try { return domain.Load(assemblyName); } catch { return null; } } } } #endif ================================================ FILE: src/Nancy/ArrayCache.cs ================================================ namespace Nancy { /// /// A cache for empty arrays. /// public class ArrayCache { /// /// Gets a cached, empty array of the specified type. /// /// the type of array to get. public static T[] Empty() { return EmptyArray.Value; } private static class EmptyArray { public static readonly T[] Value = new T[0]; } } } ================================================ FILE: src/Nancy/AsyncNamedPipelineBase.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; /// /// Abstract base class for request pipelines with async support /// /// The type of the asynchronous delegate. /// The type of the synchronus delegate. public abstract class AsyncNamedPipelineBase { /// /// Pipeline items to execute /// protected readonly List> pipelineItems; /// /// Creates a new instance of /// protected AsyncNamedPipelineBase() { this.pipelineItems = new List>(); } /// /// Creates a new instance of with size /// /// Number of delegates in pipeline protected AsyncNamedPipelineBase(int capacity) { this.pipelineItems = new List>(capacity); } /// /// Gets the current pipeline items /// public IEnumerable> PipelineItems { get { return this.pipelineItems.AsReadOnly(); } } /// /// Gets the current pipeline item delegates /// public IEnumerable PipelineDelegates { get { return this.pipelineItems.Select(pipelineItem => pipelineItem.Delegate); } } /// /// Add an item to the start of the pipeline /// /// Item to add public virtual void AddItemToStartOfPipeline(TAsyncDelegate item) { this.AddItemToStartOfPipeline((PipelineItem)item); } /// /// Add an item to the start of the pipeline /// /// Item to add public virtual void AddItemToStartOfPipeline(TSyncDelegate item) { this.AddItemToStartOfPipeline(this.Wrap(item)); } /// /// Add an item to the start of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToStartOfPipeline(PipelineItem item, bool replaceInPlace = false) { this.InsertItemAtPipelineIndex(0, item, replaceInPlace); } /// /// Add an item to the start of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToStartOfPipeline(PipelineItem item, bool replaceInPlace = false) { this.AddItemToStartOfPipeline(this.Wrap(item), replaceInPlace); } /// /// Add an item to the end of the pipeline /// /// Item to add public virtual void AddItemToEndOfPipeline(TAsyncDelegate item) { this.AddItemToEndOfPipeline((PipelineItem)item); } /// /// Add an item to the end of the pipeline /// /// Item to add public virtual void AddItemToEndOfPipeline(TSyncDelegate item) { this.AddItemToEndOfPipeline(this.Wrap(item)); } /// /// Add an item to the end of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToEndOfPipeline(PipelineItem item, bool replaceInPlace = false) { var existingIndex = this.RemoveByName(item.Name); if (replaceInPlace && existingIndex != -1) { this.InsertItemAtPipelineIndex(existingIndex, item); } else { this.pipelineItems.Add(item); } } /// /// Add an item to the end of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToEndOfPipeline(PipelineItem item, bool replaceInPlace = false) { this.AddItemToEndOfPipeline(this.Wrap(item), replaceInPlace); } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add public virtual void InsertItemAtPipelineIndex(int index, TAsyncDelegate item) { this.InsertItemAtPipelineIndex(index, (PipelineItem)item); } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add public virtual void InsertItemAtPipelineIndex(int index, TSyncDelegate item) { this.InsertItemAtPipelineIndex(index, this.Wrap(item)); } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void InsertItemAtPipelineIndex(int index, PipelineItem item, bool replaceInPlace = false) { var existingIndex = this.RemoveByName(item.Name); var newIndex = (replaceInPlace && existingIndex != -1) ? existingIndex : index; this.pipelineItems.Insert(newIndex, item); } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void InsertItemAtPipelineIndex(int index, PipelineItem item, bool replaceInPlace = false) { this.InsertItemAtPipelineIndex(index, this.Wrap(item), replaceInPlace); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, TAsyncDelegate item) { this.InsertBefore(name, (PipelineItem)item); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, TSyncDelegate item) { this.InsertBefore(name, this.Wrap(item)); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, PipelineItem item) { var existingIndex = this.pipelineItems.FindIndex(i => string.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex == -1) { existingIndex = 0; } this.InsertItemAtPipelineIndex(existingIndex, item); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, PipelineItem item) { this.InsertBefore(name, this.Wrap(item)); } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, TAsyncDelegate item) { this.InsertAfter(name, (PipelineItem)item); } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, TSyncDelegate item) { this.InsertAfter(name, this.Wrap(item)); } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, PipelineItem item) { var existingIndex = this.pipelineItems.FindIndex(i => string.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex == -1) { existingIndex = this.pipelineItems.Count; } existingIndex++; if (existingIndex > this.pipelineItems.Count) { this.AddItemToEndOfPipeline(item); } else { this.InsertItemAtPipelineIndex(existingIndex, item); } } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, PipelineItem item) { this.InsertAfter(name, this.Wrap(item)); } /// /// Remove a named pipeline item /// /// Name /// Index of item that was removed or -1 if nothing removed public virtual int RemoveByName(string name) { if (string.IsNullOrEmpty(name)) { return -1; } var existingIndex = this.pipelineItems.FindIndex(i => string.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex != -1) { this.pipelineItems.RemoveAt(existingIndex); } return existingIndex; } /// /// Wraps a sync delegate into its async form /// /// Sync pipeline instance /// Async pipeline instance protected abstract PipelineItem Wrap(PipelineItem syncDelegate); } } ================================================ FILE: src/Nancy/BeforePipeline.cs ================================================ namespace Nancy { using System; using System.Threading; using System.Threading.Tasks; /// /// Intercepts the request before it is passed to the appropriate route handler. /// This gives you a couple of possibilities such as modifying parts of the request /// or even prematurely aborting the request by returning a response that will be sent back to the caller. /// /// public class BeforePipeline : AsyncNamedPipelineBase>, Func> { /// /// Initializes a new instance of the class. /// public BeforePipeline() { } /// /// Initializes a new instance of the class. /// /// Number of delegates in pipeline public BeforePipeline(int capacity) : base(capacity) { } /// /// Performs an implicit conversion from to . /// /// The . /// /// The result of the conversion. /// public static implicit operator Func>(BeforePipeline pipeline) { return pipeline.Invoke; } /// /// Performs an implicit conversion from to . /// /// A . /// /// A new instance with . /// public static implicit operator BeforePipeline(Func> func) { var pipeline = new BeforePipeline(); pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends a new function to the . /// /// The instance. /// A /// /// with added /// public static BeforePipeline operator +(BeforePipeline pipeline, Func> func) { pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends a new action to the . /// /// The instance. /// The for appending to the instance. /// /// with added /// public static BeforePipeline operator +(BeforePipeline pipeline, Func action) { pipeline.AddItemToEndOfPipeline(action); return pipeline; } /// /// Appends the items of an to the other. /// /// The to add to. /// The to add. /// /// /// public static BeforePipeline operator +(BeforePipeline pipelineToAddTo, BeforePipeline pipelineToAdd) { foreach (var pipelineItem in pipelineToAdd.PipelineItems) { pipelineToAddTo.AddItemToEndOfPipeline(pipelineItem); } return pipelineToAddTo; } /// /// Invokes the specified . /// /// The instance. /// The instance. /// /// A instance or /// public async Task Invoke(NancyContext context, CancellationToken cancellationToken) { foreach (var pipelineDelegate in this.PipelineDelegates) { var response = await pipelineDelegate.Invoke(context, cancellationToken).ConfigureAwait(false); if (response != null) { return response; } } return null; } /// /// Wraps the specified into its async form. /// /// The . /// Async instance protected override PipelineItem>> Wrap(PipelineItem> pipelineItem) { return new PipelineItem>>(pipelineItem.Name, (ctx, ct) => Task.FromResult(pipelineItem.Delegate(ctx))); } } } ================================================ FILE: src/Nancy/Bootstrapper/BootstrapperException.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Runtime.Serialization; /// /// Exception that is raised from inside the type or one of /// the inheriting types. /// public class BootstrapperException : Exception { /// /// Initializes a new instance of the class, with /// the provided . /// /// The message that describes the error. public BootstrapperException(string message) : base(message) { } /// /// Initializes a new instance of the class, with /// the provided and . /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. public BootstrapperException(string message, Exception innerException) : base(message, innerException) { } #if !NETSTANDARD1_6 /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected BootstrapperException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } } ================================================ FILE: src/Nancy/Bootstrapper/CollectionTypeRegistration.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; /// /// Represents a type to be registered multiple times into the /// container to later be resolved using an IEnumerable{RegistrationType} /// constructor dependency. /// public class CollectionTypeRegistration : ContainerRegistration { /// /// Represents a type to be registered multiple times into the /// container to later be resolved using an IEnumerable{RegistrationType} /// constructor dependency. /// /// Registration type i.e. IMyInterface /// Collection of implementation type i.e. MyClassThatImplementsIMyInterface /// Lifetime to register the type as public CollectionTypeRegistration(Type registrationType, IEnumerable implementationTypes, Lifetime lifetime = Lifetime.Singleton) { if (registrationType == null) { throw new ArgumentNullException("registrationType"); } if (implementationTypes == null) { throw new ArgumentNullException("implementationTypes"); } this.RegistrationType = registrationType; this.ImplementationTypes = implementationTypes; this.Lifetime = lifetime; this.ValidateTypeCompatibility(implementationTypes.ToArray()); } /// /// Collection of implementation type i.e. MyClassThatImplementsIMyInterface /// public IEnumerable ImplementationTypes { get; private set; } } } ================================================ FILE: src/Nancy/Bootstrapper/ContainerRegistration.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Base class for container registrations /// public abstract class ContainerRegistration { /// /// Gets the lifetime of the registration /// public Lifetime Lifetime { get; protected set; } /// /// Registration type i.e. IMyInterface /// public Type RegistrationType { get; protected set; } /// /// Checks if all implementation types are assignable from the registration type, otherwise throws an exception. /// /// The implementation types. /// One or more of the implementation types is not assignable from the registration type. /// The property must be assigned before the method is invoked. protected void ValidateTypeCompatibility(params Type[] types) { if (this.RegistrationType == null) { throw new InvalidOperationException("The RegistrationType must be set first."); } var incompatibleTypes = types.Where(type => !this.RegistrationType.IsAssignableFrom(type) && !type.IsAssignableToGenericType(this.RegistrationType)).ToArray(); if (incompatibleTypes.Any()) { var incompatibleTypeNames = string.Join(", ", incompatibleTypes.Select(type => type.FullName)); var errorMessage = string.Format("{0} must implement {1} inorder to be registered by {2}", incompatibleTypeNames, this.RegistrationType.FullName, this.GetType().Name); throw new ArgumentException(errorMessage); } } } } ================================================ FILE: src/Nancy/Bootstrapper/FavIconApplicationStartup.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.IO; using System.Linq; using Nancy.Configuration; using System.Reflection; /// /// Application startup task that attempts to locate a favicon. The startup will first scan all /// folders in the path defined by the provided and if it cannot /// find one, it will fall back and use the default favicon that is embedded in the Nancy.dll file. /// public class FavIconApplicationStartup : IApplicationStartup { private static TraceConfiguration traceConfiguration; private static IRootPathProvider rootPathProvider; private static byte[] favIcon; /// /// Initializes a new instance of the class, with the /// provided instance. /// /// The that should be used to scan for a favicon. /// An instance. public FavIconApplicationStartup(IRootPathProvider rootPathProvider, INancyEnvironment environment) { FavIconApplicationStartup.rootPathProvider = rootPathProvider; FavIconApplicationStartup.traceConfiguration = environment.GetValue(); } /// /// Gets the default favicon /// /// A byte array, containing a favicon.ico file. public static byte[] FavIcon { get { return favIcon ?? (favIcon = ScanForFavIcon()); } } /// /// Perform any initialisation tasks /// /// Application pipelines public void Initialize(IPipelines pipelines) { } private static byte[] ExtractDefaultIcon() { var resourceStream = typeof(INancyEngine).GetTypeInfo().Assembly.GetManifestResourceStream("Nancy.favicon.ico"); if (resourceStream == null) { return null; } var result = new byte[resourceStream.Length]; resourceStream.Read(result, 0, (int)resourceStream.Length); return result; } private static byte[] LocateIconOnFileSystem() { if (rootPathProvider == null) { return null; } var extensions = new[] { "ico", "png" }; var locatedFavIcon = extensions.SelectMany(EnumerateFiles).FirstOrDefault(); if (locatedFavIcon == null) { return null; } try { return File.ReadAllBytes(locatedFavIcon); } catch (Exception e) { if (!traceConfiguration.Enabled) { throw new InvalidDataException("Unable to load favicon", e); } return null; } } private static IEnumerable EnumerateFiles(string extension) { var rootPath = rootPathProvider.GetRootPath(); var fileName = string.Concat("favicon.", extension); return Directory.EnumerateFiles(rootPath, fileName, SearchOption.AllDirectories); } private static byte[] ScanForFavIcon() { byte[] locatedIcon = null; try { locatedIcon = LocateIconOnFileSystem(); } catch (Exception) { } return locatedIcon ?? ExtractDefaultIcon(); } } } ================================================ FILE: src/Nancy/Bootstrapper/IApplicationStartup.cs ================================================ namespace Nancy.Bootstrapper { /// /// Provides a hook to execute code during application startup. /// public interface IApplicationStartup { /// /// Perform any initialisation tasks /// /// Application pipelines void Initialize(IPipelines pipelines); } } ================================================ FILE: src/Nancy/Bootstrapper/INancyBootstrapper.cs ================================================ namespace Nancy.Bootstrapper { using System; using Nancy.Configuration; /// /// Bootstrapper for the Nancy Engine /// public interface INancyBootstrapper : IDisposable { /// /// Initialise the bootstrapper. /// /// Must be called prior to and . void Initialise(); /// /// Gets the configured . /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. INancyEngine GetEngine(); /// /// Get the instance. /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. INancyEnvironment GetEnvironment(); } } ================================================ FILE: src/Nancy/Bootstrapper/IPipelines.cs ================================================ namespace Nancy.Bootstrapper { /// /// Defines the functionality of a Nancy pipeline. /// public interface IPipelines { /// /// /// The pre-request hook /// /// /// The PreRequest hook is called prior to processing a request. If a hook returns /// a non-null response then processing is aborted and the response provided is /// returned. /// /// BeforePipeline BeforeRequest { get; set; } /// /// /// The post-request hook /// /// /// The post-request hook is called after the response is created. It can be used /// to rewrite the response or add/remove items from the context. /// /// AfterPipeline AfterRequest { get; set; } /// /// /// The error hook /// /// /// The error hook is called if an exception is thrown at any time during the pipeline. /// If no error hook exists a standard InternalServerError response is returned /// /// ErrorPipeline OnError { get; set; } } } ================================================ FILE: src/Nancy/Bootstrapper/IRegistrations.cs ================================================ namespace Nancy.Bootstrapper { using System.Collections.Generic; /// /// Provides a hook to perform registrations during application startup. /// public interface IRegistrations { /// /// Gets the type registrations to register for this startup task /// IEnumerable TypeRegistrations { get; } /// /// Gets the collection registrations to register for this startup task /// IEnumerable CollectionTypeRegistrations { get; } /// /// Gets the instance registrations to register for this startup task /// IEnumerable InstanceRegistrations { get; } } } ================================================ FILE: src/Nancy/Bootstrapper/IRequestStartup.cs ================================================ namespace Nancy.Bootstrapper { /// /// Provides a hook to execute code during request startup. /// public interface IRequestStartup { /// /// Perform any initialisation tasks /// /// Application pipelines /// The current context void Initialize(IPipelines pipelines, NancyContext context); } } ================================================ FILE: src/Nancy/Bootstrapper/InstanceRegistration.cs ================================================ namespace Nancy.Bootstrapper { using System; /// /// Represents an instance to be registered into the container /// public class InstanceRegistration : ContainerRegistration { /// /// Initializes a new instance of the class. /// /// The registration type. /// The implementation. public InstanceRegistration(Type registrationType, object implementation) { if (registrationType == null) { throw new ArgumentNullException("registrationType"); } if (implementation == null) { throw new ArgumentNullException("implementation"); } this.RegistrationType = registrationType; this.Implementation = implementation; this.Lifetime = Lifetime.Singleton; this.ValidateTypeCompatibility(implementation.GetType()); } /// /// Implementation object instance i.e. instance of MyClassThatImplementsIMyInterface /// public object Implementation { get; private set; } } } ================================================ FILE: src/Nancy/Bootstrapper/Lifetime.cs ================================================ namespace Nancy.Bootstrapper { /// /// Represents the lifetime of a container registration /// public enum Lifetime { /// /// Transient lifetime - each request to the container for /// the type will result in a new version being returned. /// Transient, /// /// Singleton - each request to the container for the type /// will result in the same instance being returned. /// Singleton, /// /// PerRequest - within the context of each request each request /// for the type will result in the same instance being returned. /// Different requests will have different versions. /// PerRequest } } ================================================ FILE: src/Nancy/Bootstrapper/ModuleRegistrationType.cs ================================================ namespace Nancy.Bootstrapper { using System; /// /// Holds module type for registration into a container. /// public sealed class ModuleRegistration { /// /// Initializes a new instance of the class, with /// the provided /// /// Type of the module. public ModuleRegistration(Type moduleType) { ModuleType = moduleType; } /// /// Gets the type of the module. /// /// The type of the module. public Type ModuleType { get; private set; } } } ================================================ FILE: src/Nancy/Bootstrapper/MultipleRootPathProvidersLocatedException.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; /// /// Exception raised when the discovers more than one /// implementation in the loaded assemblies. /// public class MultipleRootPathProvidersLocatedException : BootstrapperException { private const string DefaultMessageIntroduction = @"More than one IRootPathProvider was found"; private const string DefaultMessageConclusion = @"and since we do not know which one you want to use, you need to override the RootPathProvider property on your bootstrapper and specify which one to use. Sorry for the inconvenience."; private const string DefaultMessage = DefaultMessageIntroduction + ", " + DefaultMessageConclusion; private string errorMessage; /// /// Initializes a new instance of the class. /// public MultipleRootPathProvidersLocatedException() : base(DefaultMessage) { } /// /// Initializes a new instance of the class, with /// the provided /// /// The message that describes the error. public MultipleRootPathProvidersLocatedException(string message) : base(message) { } /// /// Initializes a new instance of the class, with /// the provided and /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. public MultipleRootPathProvidersLocatedException(string message, Exception innerException) : base(message, innerException) { } /// /// Initializes a new instance of the class, with /// the provided /// /// The provider types. public MultipleRootPathProvidersLocatedException(IEnumerable providerTypes) : base(DefaultMessage) { this.StoreProviderTypes(providerTypes); } #if !NETSTANDARD1_6 /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected MultipleRootPathProvidersLocatedException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif /// /// Gets the provider types. /// /// The provider types. public IEnumerable ProviderTypes { get; internal set; } /// /// Stores the provider types. /// /// The provider types. private void StoreProviderTypes(IEnumerable providerTypes) { this.ProviderTypes = providerTypes.ToList().AsReadOnly(); this.Data.Add("ProviderTypes", this.ProviderTypes); } /// /// Returns a more friendly and informative message if the list of providers is available. /// /// /// Message generated will be of the format: /// /// More than one IRootPathProvider was found: /// Nancy.Tests.Functional.Tests.CustomRootPathProvider2 /// Nancy.Tests.Functional.Tests.CustomRootPathProvider /// and since we do not know which one you want to use, you need to override the RootPathProvider property on your bootstrapper and specify which one to use. Sorry for the inconvenience. /// /// public override string Message { get { return (this.errorMessage ?? (this.errorMessage = this.GetErrorMessage())); } } private string GetErrorMessage() { if ((this.ProviderTypes == null) || (!this.ProviderTypes.Any())) { return base.Message; } var builder = new StringBuilder(DefaultMessageIntroduction); foreach (var providerType in this.ProviderTypes) { builder.AppendFormat("\n {0}", providerType.FullName); } builder.AppendFormat("\n {0}", DefaultMessageConclusion); return builder.ToString(); } } } ================================================ FILE: src/Nancy/Bootstrapper/NancyBootstrapperBase.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Nancy.Configuration; using Nancy.Conventions; using Nancy.Cryptography; using Nancy.Diagnostics; using Nancy.Extensions; using Nancy.ModelBinding; using Nancy.Validation; using Nancy.ViewEngines; /// /// Nancy bootstrapper base class /// /// IoC container type [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1623:PropertySummaryDocumentationMustMatchAccessors", Justification = "Abstract base class - properties are described differently for overriding.")] public abstract class NancyBootstrapperBase : INancyBootstrapper, INancyModuleCatalog, IDisposable where TContainer : class { /// /// Stores whether the bootstrapper has been initialised /// prior to calling GetEngine. /// private bool initialised; /// /// Stores whether the bootstrapper is in the process of /// being disposed. /// private bool disposing; /// /// Stores the used by Nancy /// private IRootPathProvider rootPathProvider; /// /// Default Nancy conventions /// private NancyConventions conventions; /// /// Internal configuration /// private Func internalConfigurationFactory; private NancyInternalConfiguration internalConfiguration; /// /// Application pipelines. /// Pipelines are "cloned" per request so they can be modified /// at the request level. /// protected IPipelines ApplicationPipelines { get; private set; } /// /// Nancy modules - built on startup from the app domain scanner /// private ModuleRegistration[] modules; /// /// Cache of request startup task types /// protected Type[] RequestStartupTaskTypeCache { get; private set; } private IAssemblyCatalog assemblyCatalog; private ITypeCatalog typeCatalog; /// /// Initializes a new instance of the class. /// protected NancyBootstrapperBase() { this.ApplicationPipelines = new Pipelines(); } /// /// Gets the Container instance - automatically set during initialise. /// protected TContainer ApplicationContainer { get; private set; } /// /// Gets the that should be used by the application. /// /// An instance. protected virtual IAssemblyCatalog AssemblyCatalog { get { return this.assemblyCatalog ?? ( #if !CORE this.assemblyCatalog = new AppDomainAssemblyCatalog() #else this.assemblyCatalog = new DependencyContextAssemblyCatalog() #endif ); } } /// /// Gets the that should be used by the application. /// /// An instance. protected virtual ITypeCatalog TypeCatalog { get { return this.typeCatalog ?? (this.typeCatalog = new DefaultTypeCatalog(this.AssemblyCatalog)); } } /// /// Nancy internal configuration /// protected virtual Func InternalConfiguration { get { return this.internalConfigurationFactory ?? (this.internalConfigurationFactory = NancyInternalConfiguration.Default); } } /// /// Nancy conventions /// protected virtual NancyConventions Conventions { get { return this.conventions ?? (this.conventions = new NancyConventions(this.TypeCatalog)); } } /// /// Gets all available module types /// protected virtual IEnumerable Modules { get { return this.modules ?? (this.modules = this.TypeCatalog .GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy) .NotOfType() .Select(t => new ModuleRegistration(t)) .ToArray()); } } /// /// Gets the available view engine types /// protected virtual IEnumerable ViewEngines { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets the available custom model binders /// protected virtual IEnumerable ModelBinders { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets the available custom type converters /// protected virtual IEnumerable TypeConverters { get { return this.TypeCatalog.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy); } } /// /// Gets the available custom body deserializers /// protected virtual IEnumerable BodyDeserializers { get { return this.TypeCatalog.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy); } } /// /// Gets all application startup tasks /// protected virtual IEnumerable ApplicationStartupTasks { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets all request startup tasks /// protected virtual IEnumerable RequestStartupTasks { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets all registration tasks /// protected virtual IEnumerable RegistrationTasks { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets the root path provider /// protected virtual IRootPathProvider RootPathProvider { get { return this.rootPathProvider ?? (this.rootPathProvider = this.GetRootPathProvider()); } } /// /// Gets the validator factories. /// protected virtual IEnumerable ModelValidatorFactories { get { return this.TypeCatalog.GetTypesAssignableTo(); } } /// /// Gets the default favicon /// protected virtual byte[] FavIcon { get { return FavIconApplicationStartup.FavIcon; } } /// /// Gets the cryptography configuration /// protected virtual CryptographyConfiguration CryptographyConfiguration { get { return CryptographyConfiguration.Default; } } private NancyInternalConfiguration GetInitializedInternalConfiguration() { return this.internalConfiguration ?? (this.internalConfiguration = this.InternalConfiguration.Invoke(this.TypeCatalog)); } /// /// Initialise the bootstrapper. Must be called prior to GetEngine. /// public void Initialise() { var configuration = this.GetInitializedInternalConfiguration(); if (configuration == null) { throw new InvalidOperationException("Configuration cannot be null"); } if (!configuration.IsValid) { throw new InvalidOperationException("Configuration is invalid"); } this.ApplicationContainer = this.GetApplicationContainer(); this.RegisterBootstrapperTypes(this.ApplicationContainer); this.ConfigureApplicationContainer(this.ApplicationContainer); var typeRegistrations = configuration.GetTypeRegistrations(); var collectionTypeRegistrations = configuration .GetCollectionTypeRegistrations() .Concat(this.GetApplicationCollections()); // TODO - should this be after initialiseinternal? this.ConfigureConventions(this.Conventions); var conventionValidationResult = this.Conventions.Validate(); if (!conventionValidationResult.Item1) { throw new InvalidOperationException(string.Format("Conventions are invalid:\n\n{0}", conventionValidationResult.Item2)); } var instanceRegistrations = this.Conventions.GetInstanceRegistrations() .Concat(this.GetAdditionalInstances()); this.RegisterTypes(this.ApplicationContainer, typeRegistrations); this.RegisterCollectionTypes(this.ApplicationContainer, collectionTypeRegistrations); this.RegisterInstances(this.ApplicationContainer, instanceRegistrations); this.RegisterRegistrationTasks(this.GetRegistrationTasks()); var environment = this.GetEnvironmentConfigurator().ConfigureEnvironment(this.Configure); this.RegisterNancyEnvironment(this.ApplicationContainer, environment); this.RegisterModules(this.ApplicationContainer, this.Modules); foreach (var applicationStartupTask in this.GetApplicationStartupTasks().ToList()) { applicationStartupTask.Initialize(this.ApplicationPipelines); } this.ApplicationStartup(this.ApplicationContainer, this.ApplicationPipelines); this.RequestStartupTaskTypeCache = this.RequestStartupTasks.ToArray(); if (this.FavIcon != null) { this.ApplicationPipelines.BeforeRequest.AddItemToStartOfPipeline(ctx => { if (ctx.Request == null || string.IsNullOrEmpty(ctx.Request.Path)) { return null; } if (String.Equals(ctx.Request.Path, "/favicon.ico", StringComparison.OrdinalIgnoreCase)) { var response = new Response { ContentType = "image/vnd.microsoft.icon", StatusCode = HttpStatusCode.OK, Contents = s => s.Write(this.FavIcon, 0, this.FavIcon.Length) }; response.Headers["Cache-Control"] = "public, max-age=604800, must-revalidate"; return response; } return null; }); } this.GetDiagnostics().Initialize(this.ApplicationPipelines); this.initialised = true; } /// /// Configures the Nancy environment /// /// The instance to configure public virtual void Configure(INancyEnvironment environment) { } /// /// Gets the used by th. /// /// An instance. protected abstract INancyEnvironmentConfigurator GetEnvironmentConfigurator(); /// /// Gets the diagnostics for initialisation /// /// IDiagnostics implementation protected abstract IDiagnostics GetDiagnostics(); /// /// Gets all registered application startup tasks /// /// An instance containing instances. protected abstract IEnumerable GetApplicationStartupTasks(); /// /// Registers and resolves all request startup tasks /// /// Container to use /// Types to register /// An instance containing instances. protected abstract IEnumerable RegisterAndGetRequestStartupTasks(TContainer container, Type[] requestStartupTypes); /// /// Gets all registered application registration tasks /// /// An instance containing instances. protected abstract IEnumerable GetRegistrationTasks(); /// /// Get all NancyModule implementation instances /// /// The current context /// An instance containing instances. public abstract IEnumerable GetAllModules(NancyContext context); /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance public abstract INancyModule GetModule(Type moduleType, NancyContext context); /// /// Gets the configured INancyEngine /// /// Configured INancyEngine public INancyEngine GetEngine() { if (!this.initialised) { throw new InvalidOperationException("Bootstrapper is not initialised. Call Initialise before GetEngine"); } var engine = this.SafeGetNancyEngineInstance(); engine.RequestPipelinesFactory = this.InitializeRequestPipelines; return engine; } /// /// Get the instance. /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. public abstract INancyEnvironment GetEnvironment(); /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public void Dispose() { // Prevent StackOverflowException if ApplicationContainer.Dispose re-triggers this Dispose if (this.disposing) { return; } // Only dispose if we're initialised, prevents possible issue with recursive disposing. if (!this.initialised) { return; } this.disposing = true; var container = this.ApplicationContainer as IDisposable; if (container != null) { try { container.Dispose(); } catch (ObjectDisposedException) { } } Dispose(true); } /// /// Hides Equals from the overrides list /// /// Object to compare /// Boolean indicating equality public override sealed bool Equals(object obj) { return base.Equals(obj); } /// /// Hides GetHashCode from the overrides list /// /// Hash code integer public override sealed int GetHashCode() { return base.GetHashCode(); } /// /// Creates and initializes the request pipelines. /// /// The used by the request. /// An instance. protected virtual IPipelines InitializeRequestPipelines(NancyContext context) { var requestPipelines = new Pipelines(this.ApplicationPipelines); if (this.RequestStartupTaskTypeCache.Any()) { var startupTasks = this.RegisterAndGetRequestStartupTasks(this.ApplicationContainer, this.RequestStartupTaskTypeCache); foreach (var requestStartup in startupTasks) { requestStartup.Initialize(requestPipelines, context); } } this.RequestStartup(this.ApplicationContainer, requestPipelines, context); return requestPipelines; } /// /// Hides ToString from the overrides list /// /// String representation public override sealed string ToString() { return base.ToString(); } /// /// Initialise the bootstrapper - can be used for adding pre/post hooks and /// any other initialisation tasks that aren't specifically container setup /// related /// /// Container instance for resolving types if required. /// Pipelines instance to be customized if required protected virtual void ApplicationStartup(TContainer container, IPipelines pipelines) { } /// /// Initialise the request - can be used for adding pre/post hooks and /// any other per-request initialisation tasks that aren't specifically container setup /// related /// /// Container /// Current pipelines /// Current context protected virtual void RequestStartup(TContainer container, IPipelines pipelines, NancyContext context) { } /// /// Configure the application level container with any additional registrations. /// /// Container instance protected virtual void ConfigureApplicationContainer(TContainer existingContainer) { } /// /// Overrides/configures Nancy's conventions /// /// Convention object instance protected virtual void ConfigureConventions(NancyConventions nancyConventions) { } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { } /// /// Resolve INancyEngine /// /// INancyEngine implementation protected abstract INancyEngine GetEngineInternal(); /// /// Gets the application level container /// /// Container instance protected abstract TContainer GetApplicationContainer(); /// /// Registers an instance in the container. /// /// The container to register into. /// The instance to register. protected abstract void RegisterNancyEnvironment(TContainer container, INancyEnvironment environment); /// /// Register the bootstrapper's implemented types into the container. /// This is necessary so a user can pass in a populated container but not have /// to take the responsibility of registering things like INancyModuleCatalog manually. /// /// Application container to register into protected abstract void RegisterBootstrapperTypes(TContainer applicationContainer); /// /// Register the default implementations of internally used types into the container as singletons /// /// Container to register into /// Type registrations to register protected abstract void RegisterTypes(TContainer container, IEnumerable typeRegistrations); /// /// Register the various collections into the container as singletons to later be resolved /// by IEnumerable{Type} constructor dependencies. /// /// Container to register into /// Collection type registrations to register protected abstract void RegisterCollectionTypes(TContainer container, IEnumerable collectionTypeRegistrationsn); /// /// Register the given module types into the container /// /// Container to register into /// NancyModule types protected abstract void RegisterModules(TContainer container, IEnumerable moduleRegistrationTypes); /// /// Register the given instances into the container /// /// Container to register into /// Instance registration types protected abstract void RegisterInstances(TContainer container, IEnumerable instanceRegistrations); /// /// Gets any additional instance registrations that need to /// be registered into the container /// /// Collection of InstanceRegistration types private IEnumerable GetAdditionalInstances() { return new[] { new InstanceRegistration(typeof(CryptographyConfiguration), this.CryptographyConfiguration), new InstanceRegistration(typeof(NancyInternalConfiguration), this.GetInitializedInternalConfiguration()), new InstanceRegistration(typeof(IRootPathProvider), this.RootPathProvider), new InstanceRegistration(typeof(IAssemblyCatalog), this.AssemblyCatalog), new InstanceRegistration(typeof(ITypeCatalog), this.TypeCatalog), }; } /// /// Creates a list of types for the collection types that are /// required to be registered in the application scope. /// /// Collection of CollectionTypeRegistration types private IEnumerable GetApplicationCollections() { return new[] { new CollectionTypeRegistration(typeof(IViewEngine), this.ViewEngines), new CollectionTypeRegistration(typeof(IModelBinder), this.ModelBinders), new CollectionTypeRegistration(typeof(ITypeConverter), this.TypeConverters), new CollectionTypeRegistration(typeof(IBodyDeserializer), this.BodyDeserializers), new CollectionTypeRegistration(typeof(IApplicationStartup), this.ApplicationStartupTasks), new CollectionTypeRegistration(typeof(IRegistrations), this.RegistrationTasks), new CollectionTypeRegistration(typeof(IModelValidatorFactory), this.ModelValidatorFactories) }; } private INancyEngine SafeGetNancyEngineInstance() { try { return this.GetEngineInternal(); } catch (Exception ex) { throw new InvalidOperationException( "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details.", ex); } } /// /// Takes the registration tasks and calls the relevant methods to register them /// /// Registration tasks protected virtual void RegisterRegistrationTasks(IEnumerable registrationTasks) { foreach (var registrationTask in registrationTasks.ToList()) { var applicationTypeRegistrations = registrationTask.TypeRegistrations; if (applicationTypeRegistrations != null) { this.RegisterTypes(this.ApplicationContainer, applicationTypeRegistrations); } var applicationCollectionRegistrations = registrationTask.CollectionTypeRegistrations; if (applicationCollectionRegistrations != null) { this.RegisterCollectionTypes(this.ApplicationContainer, applicationCollectionRegistrations); } var applicationInstanceRegistrations = registrationTask.InstanceRegistrations; if (applicationInstanceRegistrations != null) { this.RegisterInstances(this.ApplicationContainer, applicationInstanceRegistrations); } } } private IRootPathProvider GetRootPathProvider() { var providerTypes = this.TypeCatalog .GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy) .ToArray(); if (providerTypes.Length > 1) { throw new MultipleRootPathProvidersLocatedException(providerTypes); } var providerType = providerTypes.SingleOrDefault() ?? typeof(DefaultRootPathProvider); return Activator.CreateInstance(providerType) as IRootPathProvider; } } } ================================================ FILE: src/Nancy/Bootstrapper/NancyBootstrapperLocator.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Class for locating an INancyBootstrapper implementation. /// /// Will search the app domain for a non-abstract one, and if it can't find one /// it will use the default nancy one that uses TinyIoC. /// public static class NancyBootstrapperLocator { private static INancyBootstrapper instance; private static readonly AssemblyName NancyAssemblyName = typeof(INancyEngine).GetTypeInfo().Assembly.GetName(); /// /// Gets the located bootstrapper /// public static INancyBootstrapper Bootstrapper { get { return instance ?? (instance = LocateBootstrapper()); } set { instance = value; } } private static INancyBootstrapper LocateBootstrapper() { var bootstrapperType = GetBootstrapperType(); try { return Activator.CreateInstance(bootstrapperType) as INancyBootstrapper; } catch (Exception ex) { var errorMessage = string.Format("Could not initialize bootstrapper of type '{0}'.", bootstrapperType.FullName); throw new BootstrapperException(errorMessage, ex); } } private static ITypeCatalog GetDefaultTypeCatalog() { var assemblyCatalog = GetAssemblyCatalog(); return new DefaultTypeCatalog(assemblyCatalog); } private static IReadOnlyCollection GetAvailableBootstrapperTypes(ITypeCatalog types) { return types.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy); } private static IAssemblyCatalog GetAssemblyCatalog() { #if CORE return new DependencyContextAssemblyCatalog(); #else return new AppDomainAssemblyCatalog(); #endif } #if !CORE private static bool IsNancyReferencing(Assembly assembly) { if (AssemblyName.ReferenceMatchesDefinition(assembly.GetName(), NancyAssemblyName)) { return true; } foreach (var referencedAssemblyName in assembly.GetReferencedAssemblies()) { if (AssemblyName.ReferenceMatchesDefinition(referencedAssemblyName, NancyAssemblyName)) { return true; } } return false; } #endif internal static Type GetBootstrapperType() { return GetBootstrapperType(GetDefaultTypeCatalog()); } internal static Type GetBootstrapperType(ITypeCatalog typeCatalog) { var customBootstrappers = GetAvailableBootstrapperTypes(typeCatalog); if (!customBootstrappers.Any()) { return typeof(DefaultNancyBootstrapper); } if (customBootstrappers.Count == 1) { return customBootstrappers.Single(); } Type bootstrapper; if (TryFindMostDerivedType(customBootstrappers, out bootstrapper)) { return bootstrapper; } var errorMessage = GetMultipleBootstrappersMessage(customBootstrappers); throw new BootstrapperException(errorMessage); } internal static bool TryFindMostDerivedType(IReadOnlyCollection customBootstrappers, out Type bootstrapper) { var set = new HashSet(); bootstrapper = null; if (customBootstrappers.All(b => set.Add(b.GetTypeInfo().BaseType))) { var except = customBootstrappers.Except(set).ToList(); bootstrapper = except.Count == 1 ? except[0] : null; } return bootstrapper != null; } private static string GetMultipleBootstrappersMessage(IEnumerable customBootstrappers) { var bootstrapperNames = customBootstrappers.Select(x => string.Concat(" - ", x.FullName)); var bootstrapperList = string.Join(Environment.NewLine, bootstrapperNames); return string.Join(Environment.NewLine, new[] { "Located multiple bootstrappers:", bootstrapperList, string.Empty, "Either remove unused bootstrapper types or specify which type to use." }); } } } ================================================ FILE: src/Nancy/Bootstrapper/NancyBootstrapperWithRequestContainerBase.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; /// /// Nancy bootstrapper base with per-request container support. /// Stores/retrieves the child container in the context to ensure that /// only one child container is stored per request, and that the child /// container will be disposed at the end of the request. /// /// IoC container type public abstract class NancyBootstrapperWithRequestContainerBase : NancyBootstrapperBase where TContainer : class { /// /// Initializes a new instance of the class. /// protected NancyBootstrapperWithRequestContainerBase() { this.RequestScopedTypes = ArrayCache.Empty(); this.RequestScopedCollectionTypes = ArrayCache.Empty(); } /// /// Context key for storing the child container in the context /// private readonly string contextKey = typeof(TContainer).FullName + "BootstrapperChildContainer"; /// /// Stores the module registrations to be registered into the request container /// private IEnumerable moduleRegistrationTypeCache; /// /// Stores the per-request type registrations /// private TypeRegistration[] RequestScopedTypes { get; set; } /// /// Stores the per-request collection registrations /// private CollectionTypeRegistration[] RequestScopedCollectionTypes { get; set; } /// /// Gets the context key for storing the child container in the context /// protected virtual string ContextKey { get { return this.contextKey; } } /// /// Get all implementation instances /// /// The current context /// An instance containing instances. public override sealed IEnumerable GetAllModules(NancyContext context) { var requestContainer = this.GetConfiguredRequestContainer(context); this.RegisterRequestContainerModules(requestContainer, this.moduleRegistrationTypeCache); return this.GetAllModules(requestContainer); } /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance public override sealed INancyModule GetModule(Type moduleType, NancyContext context) { var requestContainer = this.GetConfiguredRequestContainer(context); return this.GetModule(requestContainer, moduleType); } /// /// Creates and initializes the request pipelines. /// /// The used by the request. /// An instance. protected override sealed IPipelines InitializeRequestPipelines(NancyContext context) { var requestContainer = this.GetConfiguredRequestContainer(context); var requestPipelines = new Pipelines(this.ApplicationPipelines); if (this.RequestStartupTaskTypeCache.Any()) { var startupTasks = this.RegisterAndGetRequestStartupTasks(requestContainer, this.RequestStartupTaskTypeCache); foreach (var requestStartup in startupTasks) { requestStartup.Initialize(requestPipelines, context); } } this.RequestStartup(requestContainer, requestPipelines, context); return requestPipelines; } /// /// Takes the registration tasks and calls the relevant methods to register them /// /// Registration tasks protected override sealed void RegisterRegistrationTasks(IEnumerable registrationTasks) { foreach (var applicationRegistrationTask in registrationTasks.ToList()) { var applicationTypeRegistrations = applicationRegistrationTask.TypeRegistrations == null ? ArrayCache.Empty() : applicationRegistrationTask.TypeRegistrations.ToArray(); this.RegisterTypes(this.ApplicationContainer, applicationTypeRegistrations.Where(tr => tr.Lifetime != Lifetime.PerRequest)); this.RequestScopedTypes = this.RequestScopedTypes.Concat(applicationTypeRegistrations.Where(tr => tr.Lifetime == Lifetime.PerRequest) .Select(tr => new TypeRegistration(tr.RegistrationType, tr.ImplementationType, Lifetime.Singleton))) .ToArray(); var applicationCollectionRegistrations = applicationRegistrationTask.CollectionTypeRegistrations == null ? ArrayCache.Empty() : applicationRegistrationTask.CollectionTypeRegistrations.ToArray(); this.RegisterCollectionTypes(this.ApplicationContainer, applicationCollectionRegistrations.Where(tr => tr.Lifetime != Lifetime.PerRequest)); this.RequestScopedCollectionTypes = this.RequestScopedCollectionTypes.Concat(applicationCollectionRegistrations.Where(tr => tr.Lifetime == Lifetime.PerRequest) .Select(tr => new CollectionTypeRegistration(tr.RegistrationType, tr.ImplementationTypes, Lifetime.Singleton))) .ToArray(); var applicationInstanceRegistrations = applicationRegistrationTask.InstanceRegistrations; if (applicationInstanceRegistrations != null) { this.RegisterInstances(this.ApplicationContainer, applicationInstanceRegistrations); } } } /// /// Gets the per-request container /// /// Current context /// Request container instance protected TContainer GetConfiguredRequestContainer(NancyContext context) { object contextObject; context.Items.TryGetValue(this.ContextKey, out contextObject); var requestContainer = contextObject as TContainer; if (requestContainer == null) { requestContainer = this.CreateRequestContainer(context); context.Items[this.ContextKey] = requestContainer; this.ConfigureRequestContainer(requestContainer, context); this.RegisterTypes(requestContainer, this.RequestScopedTypes); this.RegisterCollectionTypes(requestContainer, this.RequestScopedCollectionTypes); } return requestContainer; } /// /// Configure the request container /// /// Request container instance /// protected virtual void ConfigureRequestContainer(TContainer container, NancyContext context) { } /// /// Register the given module types into the container /// /// Container to register into /// NancyModule types protected override sealed void RegisterModules(TContainer container, IEnumerable moduleRegistrationTypes) { this.moduleRegistrationTypeCache = moduleRegistrationTypes; } /// /// Creates a per request child/nested container /// /// Current context /// Request container instance protected abstract TContainer CreateRequestContainer(NancyContext context); /// /// Register the given module types into the request container /// /// Container to register into /// NancyModule types protected abstract void RegisterRequestContainerModules(TContainer container, IEnumerable moduleRegistrationTypes); /// /// Retrieve all module instances from the container /// /// Container to use /// Collection of NancyModule instances protected abstract IEnumerable GetAllModules(TContainer container); /// /// Retrieve a specific module instance from the container /// /// Container to use /// Type of the module /// NancyModule instance protected abstract INancyModule GetModule(TContainer container, Type moduleType); } } ================================================ FILE: src/Nancy/Bootstrapper/NancyInternalConfiguration.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; using Nancy.Configuration; using Nancy.Culture; using Nancy.Diagnostics; using Nancy.ErrorHandling; using Nancy.Localization; using Nancy.ModelBinding; using Nancy.Responses; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Routing.Constraints; using Nancy.Routing.Trie; using Nancy.Security; using Nancy.Validation; using Nancy.ViewEngines; /// /// Configuration class for Nancy's internals. /// Contains implementation types/configuration for Nancy that usually /// do not require overriding in "general use". /// public sealed class NancyInternalConfiguration { /// /// Gets the Nancy default configuration /// public static Func Default { get { return typeCatalog => new NancyInternalConfiguration { Binder = typeof(DefaultBinder), BindingDefaults = typeof(BindingDefaults), ContextFactory = typeof(DefaultNancyContextFactory), CsrfTokenValidator = typeof(DefaultCsrfTokenValidator), CultureService = typeof(DefaultCultureService), DefaultConfigurationProviders = typeCatalog.GetTypesAssignableTo().ToList(), Diagnostics = typeof(DefaultDiagnostics), EnvironmentFactory = typeof(DefaultNancyEnvironmentFactory), EnvironmentConfigurator = typeof(DefaultNancyEnvironmentConfigurator), FieldNameConverter = typeof(DefaultFieldNameConverter), InteractiveDiagnosticProviders = new List(typeCatalog.GetTypesAssignableTo()), ModelBinderLocator = typeof(DefaultModelBinderLocator), ModelValidatorLocator = typeof(DefaultValidatorLocator), NancyEngine = typeof(NancyEngine), NancyModuleBuilder = typeof(DefaultNancyModuleBuilder), ObjectSerializer = typeof(DefaultObjectSerializer), RenderContextFactory = typeof(DefaultRenderContextFactory), RequestDispatcher = typeof(DefaultRequestDispatcher), RequestTraceFactory = typeof(DefaultRequestTraceFactory), RequestTracing = typeof(DefaultRequestTracing), ResourceAssemblyProvider = typeof(ResourceAssemblyProvider), ResourceReader = typeof(DefaultResourceReader), ResponseFormatterFactory = typeof(DefaultResponseFormatterFactory), ResponseNegotiator = typeof(DefaultResponseNegotiator), ResponseProcessors = typeCatalog.GetTypesAssignableTo().ToList(), RouteCache = typeof(RouteCache), RouteCacheProvider = typeof(DefaultRouteCacheProvider), RouteInvoker = typeof(DefaultRouteInvoker), RouteResolver = typeof(DefaultRouteResolver), RouteResolverTrie = typeof(RouteResolverTrie), RouteSegmentConstraints = typeCatalog.GetTypesAssignableTo().ToList(), RouteSegmentExtractor = typeof(DefaultRouteSegmentExtractor), RouteMetadataProviders = typeCatalog.GetTypesAssignableTo().ToList(), RouteDescriptionProvider = typeof(DefaultRouteDescriptionProvider), RuntimeEnvironmentInformation = typeof(DefaultRuntimeEnvironmentInformation), SerializerFactory = typeof(DefaultSerializerFactory), Serializers = typeCatalog.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy).Union(new List(new[] { typeof(DefaultJsonSerializer), typeof(DefaultXmlSerializer) })).ToList(), StaticContentProvider = typeof(DefaultStaticContentProvider), StatusCodeHandlers = new List(typeCatalog.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy).Concat(new[] { typeof(DefaultStatusCodeHandler) })), TextResource = typeof(ResourceBasedTextResource), TrieNodeFactory = typeof(TrieNodeFactory), ViewLocator = typeof(DefaultViewLocator), ViewFactory = typeof(DefaultViewFactory), ViewResolver = typeof(DefaultViewResolver), ViewCache = typeof(DefaultViewCache), ViewLocationProvider = typeof(FileSystemViewLocationProvider), }; } } /// /// Gets or sets the runtime environment information /// public Type RuntimeEnvironmentInformation { get; set; } /// /// Gets or sets the serializer factory. /// public Type SerializerFactory { get; set; } /// /// Gets or sets the default configuration providers /// public IList DefaultConfigurationProviders { get; set; } /// /// Gets or sets the environment configurator /// public Type EnvironmentConfigurator { get; set; } /// ///Gets or sets the environment factory /// public Type EnvironmentFactory { get; set; } /// /// Gets or sets the route metadata providers /// public IList RouteMetadataProviders { get; set; } /// /// Gets or sets the route resolver /// public Type RouteResolver { get; set; } /// /// Gets or sets the context factory /// public Type ContextFactory { get; set; } /// /// Gets or sets the nancy engine /// public Type NancyEngine { get; set; } /// /// Gets or sets the route cache /// public Type RouteCache { get; set; } /// /// Gets or sets the route cache provider /// public Type RouteCacheProvider { get; set; } /// /// Gets or sets the view locator /// public Type ViewLocator { get; set; } /// /// Gets or sets the view factory /// public Type ViewFactory { get; set; } /// /// Gets or sets the nancy module builder /// public Type NancyModuleBuilder { get; set; } /// /// Gets or sets the response formatter factory /// public Type ResponseFormatterFactory { get; set; } /// /// Gets or sets themodel binder locator /// public Type ModelBinderLocator { get; set; } /// /// Gets or sets the binder /// public Type Binder { get; set; } /// /// Gets or sets the binding defaults /// public Type BindingDefaults { get; set; } /// /// Gets or sets the field name converter /// public Type FieldNameConverter { get; set; } /// /// Gets or sets the model validator locator /// public Type ModelValidatorLocator { get; set; } /// ///Gets or sets the view resolver /// public Type ViewResolver { get; set; } /// /// Gets or sets the view cache /// public Type ViewCache { get; set; } /// /// Gets or sets the render context factory /// public Type RenderContextFactory { get; set; } /// /// Gets or sets the view location provider /// public Type ViewLocationProvider { get; set; } /// /// Gets or sets the status code handlers /// public IList StatusCodeHandlers { get; set; } /// /// Gets or sets the CSRF token validator /// public Type CsrfTokenValidator { get; set; } /// /// Gets or sets the object serializer /// public Type ObjectSerializer { get; set; } /// /// Gets or sets the types for serializers /// public IList Serializers { get; set; } /// /// Gets or sets the interactive diagnostic providers /// public IList InteractiveDiagnosticProviders { get; set; } /// /// Gets or sets the request tracing /// public Type RequestTracing { get; set; } /// /// Gets or sets the route invoker /// public Type RouteInvoker { get; set; } /// /// Gets or sets the response processors /// public IList ResponseProcessors { get; set; } /// /// Gets or sets the request dispatcher /// public Type RequestDispatcher { get; set; } /// /// Gets or sets the diagnostics /// public Type Diagnostics { get; set; } /// /// Gets or sets the route segment extractor /// public Type RouteSegmentExtractor { get; set; } /// /// Gets or sets the route description provider /// public Type RouteDescriptionProvider { get; set; } /// /// Gets or sets the culture service /// public Type CultureService { get; set; } /// /// Gets or sets the text resource /// public Type TextResource { get; set; } /// /// Gets or sets the resource assembly provider /// public Type ResourceAssemblyProvider { get; set; } /// /// Gets or sets the resource reader /// public Type ResourceReader { get; set; } /// /// Gets or sets the static content provider /// public Type StaticContentProvider { get; set; } /// /// Gets or sets the route resolver trie /// public Type RouteResolverTrie { get; set; } /// /// Gets or sets the trie node factory /// public Type TrieNodeFactory { get; set; } /// /// Gets or sets the route segment constraints /// public IList RouteSegmentConstraints { get; set; } /// /// Gets or sets the request trace factory /// public Type RequestTraceFactory { get; set; } /// /// Gets or sets the response negotiator /// public Type ResponseNegotiator { get; set; } /// /// Gets a value indicating whether the configuration is valid. /// public bool IsValid { get { try { return this.GetTypeRegistrations().All(tr => tr.RegistrationType != null); } catch (ArgumentNullException) { return false; } } } /// /// Creates a new nancy internal configuration initializer with overrides for default types. /// /// Action that overrides default configuration types /// Initializer with overriden default types public static Func WithOverrides(Action builder) { return catalog => { var configuration = Default.Invoke(catalog); builder.Invoke(configuration); return configuration; }; } /// /// Returns the configuration types as a TypeRegistration collection /// /// TypeRegistration collection representing the configuration types public IEnumerable GetTypeRegistrations() { return new[] { new TypeRegistration(typeof(IRouteResolver), this.RouteResolver), new TypeRegistration(typeof(INancyEngine), this.NancyEngine), new TypeRegistration(typeof(IRouteCache), this.RouteCache), new TypeRegistration(typeof(IRouteCacheProvider), this.RouteCacheProvider), new TypeRegistration(typeof(IViewLocator), this.ViewLocator), new TypeRegistration(typeof(IViewFactory), this.ViewFactory), new TypeRegistration(typeof(INancyContextFactory), this.ContextFactory), new TypeRegistration(typeof(INancyModuleBuilder), this.NancyModuleBuilder), new TypeRegistration(typeof(IResponseFormatterFactory), this.ResponseFormatterFactory), new TypeRegistration(typeof(IModelBinderLocator), this.ModelBinderLocator), new TypeRegistration(typeof(IBinder), this.Binder), new TypeRegistration(typeof(BindingDefaults), this.BindingDefaults), new TypeRegistration(typeof(IFieldNameConverter), this.FieldNameConverter), new TypeRegistration(typeof(IViewResolver), this.ViewResolver), new TypeRegistration(typeof(IViewCache), this.ViewCache), new TypeRegistration(typeof(IRenderContextFactory), this.RenderContextFactory), new TypeRegistration(typeof(IViewLocationProvider), this.ViewLocationProvider), new TypeRegistration(typeof(ICsrfTokenValidator), this.CsrfTokenValidator), new TypeRegistration(typeof(IObjectSerializer), this.ObjectSerializer), new TypeRegistration(typeof(IModelValidatorLocator), this.ModelValidatorLocator), new TypeRegistration(typeof(IRequestTracing), this.RequestTracing), new TypeRegistration(typeof(IRouteInvoker), this.RouteInvoker), new TypeRegistration(typeof(IRequestDispatcher), this.RequestDispatcher), new TypeRegistration(typeof(IDiagnostics), this.Diagnostics), new TypeRegistration(typeof(IRouteSegmentExtractor), this.RouteSegmentExtractor), new TypeRegistration(typeof(IRouteDescriptionProvider), this.RouteDescriptionProvider), new TypeRegistration(typeof(ICultureService), this.CultureService), new TypeRegistration(typeof(ITextResource), this.TextResource), new TypeRegistration(typeof(IResourceAssemblyProvider), this.ResourceAssemblyProvider), new TypeRegistration(typeof(IResourceReader), this.ResourceReader), new TypeRegistration(typeof(IStaticContentProvider), this.StaticContentProvider), new TypeRegistration(typeof(IRouteResolverTrie), this.RouteResolverTrie), new TypeRegistration(typeof(ITrieNodeFactory), this.TrieNodeFactory), new TypeRegistration(typeof(IRequestTraceFactory), this.RequestTraceFactory), new TypeRegistration(typeof(IResponseNegotiator), this.ResponseNegotiator), new TypeRegistration(typeof(INancyEnvironmentConfigurator), this.EnvironmentConfigurator), new TypeRegistration(typeof(INancyEnvironmentFactory), this.EnvironmentFactory), new TypeRegistration(typeof(ISerializerFactory), this.SerializerFactory), new TypeRegistration(typeof(IRuntimeEnvironmentInformation), this.RuntimeEnvironmentInformation) }; } /// /// Returns the collection configuration types as a CollectionTypeRegistration collection /// /// CollectionTypeRegistration collection representing the configuration types public IEnumerable GetCollectionTypeRegistrations() { return new[] { new CollectionTypeRegistration(typeof(IResponseProcessor), this.ResponseProcessors), new CollectionTypeRegistration(typeof(ISerializer), this.Serializers), new CollectionTypeRegistration(typeof(IStatusCodeHandler), this.StatusCodeHandlers), new CollectionTypeRegistration(typeof(IDiagnosticsProvider), this.InteractiveDiagnosticProviders), new CollectionTypeRegistration(typeof(IRouteSegmentConstraint), this.RouteSegmentConstraints), new CollectionTypeRegistration(typeof(IRouteMetadataProvider), this.RouteMetadataProviders), new CollectionTypeRegistration(typeof(INancyDefaultConfigurationProvider), this.DefaultConfigurationProviders), }; } } } ================================================ FILE: src/Nancy/Bootstrapper/Pipelines.cs ================================================ namespace Nancy.Bootstrapper { using System.Linq; /// /// Default implementation of the Nancy pipelines /// public class Pipelines : IPipelines { /// /// Initializes a new instance of the class. /// public Pipelines() { this.AfterRequest = new AfterPipeline(); this.BeforeRequest = new BeforePipeline(); this.OnError = new ErrorPipeline(); } /// /// Initializes a new instance of the class and clones the hooks from /// the provided instance. /// public Pipelines(IPipelines pipelines) { this.AfterRequest = new AfterPipeline(pipelines.AfterRequest.PipelineItems.Count()); foreach (var pipelineItem in pipelines.AfterRequest.PipelineItems) { this.AfterRequest.AddItemToEndOfPipeline(pipelineItem); } this.BeforeRequest = new BeforePipeline(pipelines.BeforeRequest.PipelineItems.Count()); foreach (var pipelineItem in pipelines.BeforeRequest.PipelineItems) { this.BeforeRequest.AddItemToEndOfPipeline(pipelineItem); } this.OnError = new ErrorPipeline(pipelines.OnError.PipelineItems.Count()); foreach (var pipelineItem in pipelines.OnError.PipelineItems) { this.OnError.AddItemToEndOfPipeline(pipelineItem); } } /// /// /// The pre-request hook /// /// /// The PreRequest hook is called prior to processing a request. If a hook returns /// a non-null response then processing is aborted and the response provided is /// returned. /// /// public BeforePipeline BeforeRequest { get; set; } /// /// /// The post-request hook /// /// /// The post-request hook is called after the response is created. It can be used /// to rewrite the response or add/remove items from the context. /// /// public AfterPipeline AfterRequest { get; set; } /// /// /// The error hook /// /// /// The error hook is called if an exception is thrown at any time during the pipeline. /// If no error hook exists a standard InternalServerError response is returned /// /// public ErrorPipeline OnError { get; set; } } } ================================================ FILE: src/Nancy/Bootstrapper/Registrations.cs ================================================ namespace Nancy.Bootstrapper { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Helper class for providing application registrations /// public abstract class Registrations : IRegistrations { private readonly ITypeCatalog typeCatalog; private readonly IList collectionRegistrations = new List(); private readonly IList instanceRegistrations = new List(); private readonly IList typeRegistrations = new List(); /// /// Initializes a new instance of the class. /// /// An instance. protected Registrations(ITypeCatalog typeCatalog) { this.typeCatalog = typeCatalog; } /// /// Gets the collection registrations to register for this startup task /// public IEnumerable CollectionTypeRegistrations { get { return this.collectionRegistrations; } } /// /// Gets the instance registrations to register for this startup task /// public IEnumerable InstanceRegistrations { get { return this.instanceRegistrations; } } /// /// Gets the type registrations to register for this startup task /// public IEnumerable TypeRegistrations { get { return this.typeRegistrations; } } /// /// Scans for the implementation of and registers it. /// /// Lifetime of the registration, defaults to singleton /// The to scan for and register as. public void Register(Lifetime lifetime = Lifetime.Singleton) { var implementation = this.typeCatalog .GetTypesAssignableTo() .Single(); this.typeRegistrations.Add(new TypeRegistration(typeof(TRegistration), implementation, lifetime)); } /// /// Scans for all implementations of and registers them. /// /// Lifetime of the registration, defaults to singleton /// The to scan for and register as. public void RegisterAll(Lifetime lifetime = Lifetime.Singleton) { var implementations = this.typeCatalog .GetTypesAssignableTo(); var registration = new CollectionTypeRegistration(typeof(TRegistration), implementations, lifetime); this.collectionRegistrations.Add(registration); } /// /// Registers the types provided by the parameters /// as . /// /// The to register as. /// The types to register. /// Lifetime of the registration, defaults to singleton public void Register(IEnumerable defaultImplementations, Lifetime lifetime = Lifetime.Singleton) { this.collectionRegistrations.Add(new CollectionTypeRegistration(typeof(TRegistration), defaultImplementations, lifetime)); } /// /// Registers the type provided by the parameter /// as . /// /// Lifetime of the registration, defaults to singleton /// The to register as. /// The to register as . public void Register(Type implementation, Lifetime lifetime = Lifetime.Singleton) { this.typeRegistrations.Add(new TypeRegistration(typeof(TRegistration), implementation, lifetime)); } /// /// Registers an instance as . /// /// The to register as. /// The instance to register. public void Register(TRegistration instance) { this.instanceRegistrations.Add(new InstanceRegistration(typeof(TRegistration), instance)); } /// /// Scans for a that implements . If found, then it /// will be used for the registration, else it will use . /// /// Lifetime of the registration, defaults to singleton /// The to register as. /// The implementation of that will be use if no other implementation can be found. /// /// When scanning, it will exclude the assembly that the instance is defined in and it will also ignore /// the type specified by . /// public void RegisterWithDefault(Type defaultImplementation, Lifetime lifetime = Lifetime.Singleton) { var implementation = this.typeCatalog .GetTypesAssignableTo() .Where(type => type.GetTypeInfo().Assembly != this.GetType().GetTypeInfo().Assembly) .SingleOrDefault(type => type != defaultImplementation); this.typeRegistrations.Add(new TypeRegistration(typeof(TRegistration), implementation ?? defaultImplementation, lifetime)); } /// /// Scans for an implementation of and registers it if found. If no implementation could /// be found, it will retrieve an instance of using the provided , /// which will be used in the registration. /// /// The to register as. /// Factory that provides an instance of . /// When scanning, it will exclude the assembly that the instance is defined in public void RegisterWithDefault(Func defaultImplementationFactory) { var implementation = this.typeCatalog .GetTypesAssignableTo() .SingleOrDefault(type => type.GetTypeInfo().Assembly != this.GetType().GetTypeInfo().Assembly); if (implementation != null) { this.typeRegistrations.Add(new TypeRegistration(typeof(TRegistration), implementation)); } else { this.instanceRegistrations.Add(new InstanceRegistration(typeof(TRegistration), defaultImplementationFactory.Invoke())); } } /// /// Scans for all implementations of . If no implementations could be found, then it /// will register the types specified by . /// /// Lifetime of the registration, defaults to singleton /// The to register as. /// The types to register if non could be located while scanning. /// /// When scanning, it will exclude the assembly that the instance is defined in and it will also ignore /// the types specified by . /// public void RegisterWithDefault(IEnumerable defaultImplementations, Lifetime lifetime = Lifetime.Singleton) { var implementations = this.typeCatalog .GetTypesAssignableTo() .Where(type => type.GetAssembly() != this.GetType().GetTypeInfo().Assembly) .Where(type => !defaultImplementations.Contains(type)) .ToList(); if (!implementations.Any()) { implementations = defaultImplementations.ToList(); } this.collectionRegistrations.Add(new CollectionTypeRegistration(typeof(TRegistration), implementations, lifetime)); } /// /// Scans for all implementations of and registers them, followed by the /// types defined by the parameter. /// /// The to register as. /// The types to register last. /// Lifetime of the registration, defaults to singleton /// /// When scanning, it will exclude the assembly that the instance is defined in and it will also ignore /// the types specified by . /// public void RegisterWithUserThenDefault(IEnumerable defaultImplementations, Lifetime lifetime = Lifetime.Singleton) { var implementations = this.typeCatalog .GetTypesAssignableTo() .Where(type => type.GetAssembly() != this.GetType().GetTypeInfo().Assembly) .Where(type => !defaultImplementations.Contains(type)) .ToList(); this.collectionRegistrations.Add(new CollectionTypeRegistration(typeof(TRegistration), implementations.Union(defaultImplementations), lifetime)); } } } ================================================ FILE: src/Nancy/Bootstrapper/TypeRegistration.cs ================================================ namespace Nancy.Bootstrapper { using System; /// /// Represents a type to be registered into the container /// public sealed class TypeRegistration : ContainerRegistration { /// /// Represents a type to be registered into the container /// /// Registration type i.e. IMyInterface /// Implementation type i.e. MyClassThatImplementsIMyInterface /// Lifetime to register the type as public TypeRegistration(Type registrationType, Type implementationType, Lifetime lifetime = Lifetime.Singleton) { if (registrationType == null) { throw new ArgumentNullException("registrationType"); } if (implementationType == null) { throw new ArgumentNullException("implementationType"); } this.RegistrationType = registrationType; this.ImplementationType = implementationType; this.Lifetime = lifetime; this.ValidateTypeCompatibility(implementationType); } /// /// Implementation type i.e. MyClassThatImplementsIMyInterface /// public Type ImplementationType { get; private set; } } } ================================================ FILE: src/Nancy/Configuration/ConfigurationException.cs ================================================ namespace Nancy { using System; using Nancy.Configuration; /// /// An exception related to an invalid configuration created within /// public class ConfigurationException : Exception { /// /// Create an instance of /// /// A message to be passed into the exception public ConfigurationException(string message) : base(message) { } /// /// Create an instance of /// /// A message to be passed into the exception /// An inner exception to buble up public ConfigurationException(string message, Exception exception) : base(message, exception) { } } } ================================================ FILE: src/Nancy/Configuration/DefaultNancyEnvironment.cs ================================================ namespace Nancy.Configuration { using System; using System.Collections; using System.Collections.Generic; /// /// Default implementation of the interface. /// public class DefaultNancyEnvironment : INancyEnvironment { private readonly IDictionary values = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// Returns an enumerator that iterates through the environment. /// /// A that can be used to iterate through the environment. public IEnumerator> GetEnumerator() { return this.values.GetEnumerator(); } /// /// Returns an enumerator that iterates through the environment. /// /// An object that can be used to iterate through the environment. IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// /// Gets the number of elements in the environment. /// /// The number of elements in the environment. public int Count { get { return this.values.Count; } } /// /// Determines whether the environment contains an element that has the specified key. /// /// if the environment contains an element that has the specified key; otherwise, . /// /// The key to retrieve. public bool ContainsKey(string key) { return this.values.ContainsKey(key); } /// /// Gets the value that is associated with the specified key. /// /// if the environment contains an element that has the specified key; otherwise, . /// The key to locate. /// 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 parameter. This parameter is passed uninitialized. bool IReadOnlyDictionary.TryGetValue(string key, out object value) { return this.values.TryGetValue(key, out value); } /// /// Gets the element that has the specified key in the environment. /// /// The element that has the specified key in the environment. /// The key to locate. object IReadOnlyDictionary.this[string key] { get { return this.values[key]; } } /// /// Gets an enumerable collection that contains the keys in the environment. /// /// An enumerable collection that contains the keys in the environment. public IEnumerable Keys { get { return this.values.Keys; } } /// /// Gets an enumerable collection that contains the values in the environment. /// /// An enumerable collection that contains the values in the environment. public IEnumerable Values { get { return this.values.Values; } } /// /// Adds a , using a provided , to the environment. /// /// The of the value to add. /// The key to store the value as. /// The value to store in the environment. public void AddValue(string key, T value) { this.values.Add(key, value); } /// /// Gets the value that is associated with the specified key. /// /// The of the value to retrieve. /// The key to get the value for. /// 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. This parameter is passed uninitialized. /// if the value could be retrieved, otherwise . public bool TryGetValue(string key, out T value) { object objectValue; if (this.values.TryGetValue(key, out objectValue)) { value = (T) objectValue; return true; } value = default(T); return false; } } } ================================================ FILE: src/Nancy/Configuration/DefaultNancyEnvironmentConfigurator.cs ================================================ namespace Nancy.Configuration { using System; using System.Collections.Generic; /// /// Default implementation of the interface. /// public class DefaultNancyEnvironmentConfigurator : INancyEnvironmentConfigurator { private readonly INancyEnvironmentFactory factory; private readonly IEnumerable defaultConfigurationProviders; /// /// Initializes a new instance of the class. /// /// The instance to use when configuring an environment. /// instances that should be used during the configuration of the environment. public DefaultNancyEnvironmentConfigurator(INancyEnvironmentFactory factory, IEnumerable defaultConfigurationProviders) { this.factory = factory; this.defaultConfigurationProviders = defaultConfigurationProviders; } /// /// Configures an instance. /// /// The configuration to apply to the environment. /// An instance. public INancyEnvironment ConfigureEnvironment(Action configuration) { var environment = this.factory.CreateEnvironment(); configuration.Invoke(environment); foreach (var configurationProvider in this.defaultConfigurationProviders) { var defaultConfiguration = SafeGetDefaultConfiguration(configurationProvider); if (defaultConfiguration == null) { continue; } var configurationKey = configurationProvider.Key; if (environment.ContainsKey(configurationKey)) { continue; } environment.AddValue(configurationKey, defaultConfiguration); } return environment; } private static object SafeGetDefaultConfiguration(INancyDefaultConfigurationProvider configurationProvider) { try { return configurationProvider.GetDefaultConfiguration(); } catch(ConfigurationException) { throw; } catch (Exception ex) { throw new ConfigurationException(string.Format("Error loading default configuration for {0}", configurationProvider.Key), ex); } } } } ================================================ FILE: src/Nancy/Configuration/DefaultNancyEnvironmentFactory.cs ================================================ namespace Nancy.Configuration { /// /// Default implementation of the interface. /// /// Creates instances of the type. public class DefaultNancyEnvironmentFactory : INancyEnvironmentFactory { /// /// Creates a new instance. /// /// A instance. public INancyEnvironment CreateEnvironment() { return new DefaultNancyEnvironment(); } } } ================================================ FILE: src/Nancy/Configuration/INancyDefaultConfigurationProvider.cs ================================================ namespace Nancy.Configuration { /// /// Defines the functionality for providing default configuration values to the . /// public interface INancyDefaultConfigurationProvider : IHideObjectMembers { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance object GetDefaultConfiguration(); /// /// Gets the key that will be used to store the configuration object in the . /// /// A containing the key. string Key { get; } } } ================================================ FILE: src/Nancy/Configuration/INancyEnvironment.cs ================================================ namespace Nancy.Configuration { using System; using System.Collections.Generic; /// /// Defines the functionality of a Nancy environment. /// public interface INancyEnvironment : IReadOnlyDictionary, IHideObjectMembers { /// /// Adds a , using a provided , to the environment. /// /// The of the value to add. /// The key to store the value as. /// The value to store in the environment. void AddValue(string key, T value); /// /// Gets the value that is associated with the specified key. /// /// The of the value to retrieve. /// The key to get the value for. /// 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. This parameter is passed uninitialized. /// if the value could be retrieved, otherwise . bool TryGetValue(string key, out T value); } } ================================================ FILE: src/Nancy/Configuration/INancyEnvironmentConfigurator.cs ================================================ namespace Nancy.Configuration { using System; /// /// Defines the functionality for applying configuration to an instance. /// public interface INancyEnvironmentConfigurator : IHideObjectMembers { /// /// Configures an instance. /// /// The configuration to apply to the environment. /// An instance. INancyEnvironment ConfigureEnvironment(Action configuration); } } ================================================ FILE: src/Nancy/Configuration/INancyEnvironmentExtensions.cs ================================================ namespace Nancy.Configuration { using System; /// /// Contains extensions for the type. /// public static class INancyEnvironmentExtensions { /// /// Adds a value to the environment, using the full name of the type defined by as the key. /// /// The instance. /// The value to store in the environment. /// The of the value to store in the environment. public static void AddValue(this INancyEnvironment environment, T value) { environment.AddValue(typeof(T).FullName, value); } /// /// Gets a value from the environment, using the full name of the type defined by as the key. /// /// The instance. /// The of the value to retreive from the environment. /// public static T GetValue(this INancyEnvironment environment) { return environment.GetValue(typeof(T).FullName); } /// /// Gets a value from the environment, using the provided . /// /// The instance. /// The key to retrieve the value for. /// The of the value to retreive from the environment. /// The stored value. public static T GetValue(this INancyEnvironment environment, string key) { return (T) environment[key]; } /// /// Gets a value from the environment, using the full name of the type defined by as the key. If /// the value could not be found, then a provided default value is returned. /// /// The instance. /// The value to return if no stored value could be found. /// The of the value to retreive from the environment. /// The stored value. public static T GetValueWithDefault(this INancyEnvironment environment, T defaultValue) { T value; return environment.TryGetValue(typeof(T).FullName, out value) ? value : defaultValue; } /// /// Gets a value from the environment, using the provided . If the value could not be found, then /// a provided default value is returned. /// /// The instance. /// The key to retrieve the value for. /// The value to return if no stored value could be found. /// The of the value to retreive from the environment. /// The stored value. public static T GetValueWithDefault(this INancyEnvironment environment, string key, T defaultValue) { T value; return environment.TryGetValue(key, out value) ? value : defaultValue; } } } ================================================ FILE: src/Nancy/Configuration/INancyEnvironmentFactory.cs ================================================ namespace Nancy.Configuration { /// /// Defines the functionality for creating a instance. /// public interface INancyEnvironmentFactory : IHideObjectMembers { /// /// Creates a new instance. /// /// A instance. INancyEnvironment CreateEnvironment(); } } ================================================ FILE: src/Nancy/Configuration/NancyDefaultConfigurationProvider.cs ================================================ namespace Nancy.Configuration { /// /// Default (abstract) implementation of interface. /// /// The type of the configuration object. public abstract class NancyDefaultConfigurationProvider : INancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance public abstract T GetDefaultConfiguration(); /// /// Gets the default configuration instance to register in the . /// /// The configuration instance object INancyDefaultConfigurationProvider.GetDefaultConfiguration() { return this.GetDefaultConfiguration(); } /// /// Gets the full type name of . /// /// A containing the key. public virtual string Key { get { return typeof(T).FullName; } } } } ================================================ FILE: src/Nancy/Conventions/AcceptHeaderCoercionConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections; using System.Collections.Generic; /// /// Collection of accept header coercions /// public class AcceptHeaderCoercionConventions : IEnumerable>, NancyContext, IEnumerable>>> { private readonly IList>, NancyContext, IEnumerable>>> conventions; /// /// Initializes a new instance of the class., with /// the provided /// /// The conventions. public AcceptHeaderCoercionConventions(IList>, NancyContext, IEnumerable>>> conventions) { this.conventions = conventions; } /// /// Returns an enumerator that iterates through the collection. /// /// /// An enumerator that can be used to iterate through the collection. /// public IEnumerator>, NancyContext, IEnumerable>>> GetEnumerator() { return this.conventions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: src/Nancy/Conventions/BuiltInAcceptHeaderCoercions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; using System.Linq; /// /// Built in functions for coercing accept headers. /// /// public static class BuiltInAcceptHeaderCoercions { private const string HtmlContentType = "text/html"; private static readonly IEnumerable> DefaultAccept = new[] { Tuple.Create(HtmlContentType, 1.0m), Tuple.Create("*/*", 0.9m) }; private static readonly string[] BrokenBrowsers = new[] {"MSIE 8", "MSIE 7", "MSIE 6", "AppleWebKit"}; /// /// Adds a default accept header if there isn't one. /// /// Current headers /// Context /// Modified headers or original if no modification required public static IEnumerable> CoerceBlankAcceptHeader(IEnumerable> currentAcceptHeaders, NancyContext context) { var current = currentAcceptHeaders as Tuple[] ?? currentAcceptHeaders.ToArray(); return !current.Any() ? DefaultAccept : current; } /// /// Replaces the accept header of stupid browsers that request XML instead /// of HTML. /// /// Current headers /// Context /// Modified headers or original if no modification required public static IEnumerable> CoerceStupidBrowsers(IEnumerable> currentAcceptHeaders, NancyContext context) { var current = currentAcceptHeaders as Tuple[] ?? currentAcceptHeaders.ToArray(); return IsStupidBrowser(current, context) ? DefaultAccept : current; } /// /// Boosts the priority of HTML for browsers that ask for xml and html with the /// same priority. /// /// Current headers /// Context /// Modified headers or original if no modification required public static IEnumerable> BoostHtml(IEnumerable> currentAcceptHeaders, NancyContext context) { var current = currentAcceptHeaders as Tuple[] ?? currentAcceptHeaders.ToArray(); var html = current.FirstOrDefault(h => string.Equals(h.Item1, HtmlContentType, StringComparison.OrdinalIgnoreCase) && h.Item2 < 1.0m); if (html == null) { return current; } var index = Array.IndexOf(current, html); if (index == -1) { return current; } current[index] = Tuple.Create(HtmlContentType, html.Item2 + 0.2m); return current.OrderByDescending(x => x.Item2).ToArray(); } private static bool IsStupidBrowser(Tuple[] current, NancyContext context) { // If there's one or less accept headers then we can't be a stupid // browser so just bail out early if (current.Length <= 1) { return false; } var maxScore = current.First().Item2; if (IsPotentiallyBrokenBrowser(context.Request.Headers.UserAgent) && !current.Any(h => h.Item2 == maxScore && string.Equals(HtmlContentType, h.Item1, StringComparison.OrdinalIgnoreCase))) { return true; } return false; } private static bool IsPotentiallyBrokenBrowser(string userAgent) { return BrokenBrowsers.Any(userAgent.Contains); } } } ================================================ FILE: src/Nancy/Conventions/BuiltInCultureConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using Nancy.Configuration; using Nancy.Session; /// /// Built in functions for determining current culture /// /// public static class BuiltInCultureConventions { /// /// Checks to see if the Form has a CurrentCulture key. /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Form otherwise null public static CultureInfo FormCulture(NancyContext context, GlobalizationConfiguration configuration) { if (context.Request.Form["CurrentCulture"] != null) { string cultureLetters = context.Request.Form["CurrentCulture"]; if (!IsValidCultureInfoName(cultureLetters, configuration)) { return null; } return new CultureInfo(cultureLetters); } return null; } /// /// Checks to see if the first argument in the Path can be used to make a CultureInfo. /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Path otherwise null public static CultureInfo PathCulture(NancyContext context, GlobalizationConfiguration configuration) { var segments = context.Request.Url.Path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); var firstSegment = segments.FirstOrDefault(); if (firstSegment != null && IsValidCultureInfoName(firstSegment, configuration)) { context.Request.Url.Path = string.Concat("/", string.Join("/", segments.Skip(1))); return new CultureInfo(firstSegment); } return null; } /// /// Checks to see if the AcceptLanguage in the Headers can be used to make a CultureInfo. Uses highest weighted if multiple defined. /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Headers otherwise null public static CultureInfo HeaderCulture(NancyContext context, GlobalizationConfiguration configuration) { if (context.Request.Headers.AcceptLanguage.Any()) { var cultureLetters = context.Request.Headers.AcceptLanguage.First().Item1; if (!IsValidCultureInfoName(cultureLetters, configuration)) { return null; } return new CultureInfo(cultureLetters); } return null; } /// /// Checks to see if the Session has a CurrentCulture key /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Session otherwise null public static CultureInfo SessionCulture(NancyContext context, GlobalizationConfiguration configuration) { var sessionType = context.Request.Session as NullSessionProvider; if (sessionType == null && context.Request.Session["CurrentCulture"] != null) { return (CultureInfo)context.Request.Session["CurrentCulture"]; } return null; } /// /// Checks to see if the Cookies has a CurrentCulture key /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Cookies otherwise null public static CultureInfo CookieCulture(NancyContext context, GlobalizationConfiguration configuration) { string cookieCulture = null; if (context.Request.Cookies.TryGetValue("CurrentCulture", out cookieCulture)) { if (!IsValidCultureInfoName(cookieCulture, configuration)) { return null; } return new CultureInfo(cookieCulture); } return null; } /// /// Checks to see if a default culture has been set on /// /// NancyContext /// Culture configuration that contains allowed cultures /// CultureInfo if found in Default Culture else null public static CultureInfo GlobalizationConfigurationCulture(NancyContext context, GlobalizationConfiguration configuration) { if (configuration.DefaultCulture != null) { if (!IsValidCultureInfoName(configuration.DefaultCulture, configuration)) { return null; } return new CultureInfo(configuration.DefaultCulture); } return null; } /// /// Validates culture name /// /// Culture name eg\en-GB /// Culture configuration that contains allowed cultures /// True/False if valid culture public static bool IsValidCultureInfoName(string name, GlobalizationConfiguration configuration) { if (string.IsNullOrWhiteSpace(name)) { return false; } return configuration.SupportedCultureNames.Contains(name); } } } ================================================ FILE: src/Nancy/Conventions/CultureConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections; using System.Collections.Generic; using System.Globalization; /// /// Collection class for static culture conventions /// /// public class CultureConventions : IEnumerable> { private readonly IEnumerable> conventions; /// /// Initializes a new instance of the class, with /// the provided . /// /// The conventions. public CultureConventions(IEnumerable> conventions) { this.conventions = conventions; } /// /// Returns an enumerator that iterates through the collection. /// /// /// An enumerator that can be used to iterate through the collection. /// public IEnumerator> GetEnumerator() { return this.conventions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } ================================================ FILE: src/Nancy/Conventions/DefaultAcceptHeaderCoercionConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; /// /// Wires up the default conventions for the accept header coercion /// public class DefaultAcceptHeaderCoercionConventions : IConvention { /// /// Initialise any conventions this class "owns" /// /// Convention object instance public void Initialise(NancyConventions conventions) { this.ConfigureDefaultConventions(conventions); } /// /// Asserts that the conventions that this class "owns" are valid /// /// Conventions object instance /// /// Tuple containing true/false for valid/invalid, and any error messages /// public Tuple Validate(NancyConventions conventions) { if (conventions.AcceptHeaderCoercionConventions == null) { return Tuple.Create(false, "The accept header coercion conventions cannot be null."); } return Tuple.Create(true, string.Empty); } private void ConfigureDefaultConventions(NancyConventions conventions) { conventions.AcceptHeaderCoercionConventions = new List>, NancyContext, IEnumerable>>>(2) { BuiltInAcceptHeaderCoercions.BoostHtml, BuiltInAcceptHeaderCoercions.CoerceBlankAcceptHeader, }; } } } ================================================ FILE: src/Nancy/Conventions/DefaultCultureConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; using System.Globalization; /// /// Default implementation of /// public class DefaultCultureConventions : IConvention { /// /// Initialise culture conventions /// /// public void Initialise(NancyConventions conventions) { ConfigureDefaultConventions(conventions); } /// /// Determine if culture conventions are valid /// /// /// public Tuple Validate(NancyConventions conventions) { if (conventions.CultureConventions == null) { return Tuple.Create(false, "The culture conventions cannot be null."); } return (conventions.CultureConventions.Count > 0) ? Tuple.Create(true, string.Empty) : Tuple.Create(false, "The culture conventions cannot be empty."); } /// /// Setup default conventions /// /// private static void ConfigureDefaultConventions(NancyConventions conventions) { conventions.CultureConventions = new List> { BuiltInCultureConventions.FormCulture, BuiltInCultureConventions.HeaderCulture, BuiltInCultureConventions.SessionCulture, BuiltInCultureConventions.CookieCulture, BuiltInCultureConventions.GlobalizationConfigurationCulture, }; } } } ================================================ FILE: src/Nancy/Conventions/DefaultStaticContentsConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; /// /// Defines the default static contents conventions. /// public class DefaultStaticContentsConventions : IConvention { /// /// Initialise any conventions this class "owns". /// /// Convention object instance. public void Initialise(NancyConventions conventions) { conventions.StaticContentsConventions = new List> { StaticContentConventionBuilder.AddDirectory("Content") }; } /// /// Asserts that the conventions that this class "owns" are valid /// /// Conventions object instance. /// Tuple containing true/false for valid/invalid, and any error messages. public Tuple Validate(NancyConventions conventions) { if (conventions.StaticContentsConventions == null) { return Tuple.Create(false, "The static contents conventions cannot be null."); } return (conventions.StaticContentsConventions.Count > 0) ? Tuple.Create(true, string.Empty) : Tuple.Create(false, "The static contents conventions cannot be empty."); } } } ================================================ FILE: src/Nancy/Conventions/DefaultViewLocationConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; using Nancy.ViewEngines; /// /// Defines the default static contents conventions. /// public class DefaultViewLocationConventions : IConvention { /// /// Initialise any conventions this class "owns". /// /// Convention object instance. public void Initialise(NancyConventions conventions) { ConfigureViewLocationConventions(conventions); } /// /// Asserts that the conventions that this class "owns" are valid. /// /// Conventions object instance. /// Tuple containing true/false for valid/invalid, and any error messages. public Tuple Validate(NancyConventions conventions) { if (conventions.ViewLocationConventions == null) { return Tuple.Create(false, "The view conventions cannot be null."); } return (conventions.ViewLocationConventions.Count > 0) ? Tuple.Create(true, string.Empty) : Tuple.Create(false, "The view conventions cannot be empty."); } private static void ConfigureViewLocationConventions(NancyConventions conventions) { conventions.ViewLocationConventions = new List> { (viewName, model, viewLocationContext) =>{ if (string.IsNullOrEmpty(viewLocationContext.ModulePath)) { return string.Empty; } var path = viewLocationContext.ModulePath.TrimStart(new[] { '/' }); return string.Concat("views/", path, "/", viewLocationContext.ModuleName, "/", viewName, "-", viewLocationContext.Context.Culture); }, // 0 Handles: views / *modulepath* / *modulename* / *viewname* (viewName, model, viewLocationContext) =>{ if (string.IsNullOrEmpty(viewLocationContext.ModulePath)) { return string.Empty; } var path = viewLocationContext.ModulePath.TrimStart(new[] { '/' }); return string.Concat("views/", path, "/", viewLocationContext.ModuleName, "/", viewName); }, (viewName, model, viewLocationContext) =>{ if (string.IsNullOrEmpty(viewLocationContext.ModulePath)) { return string.Empty; } var path = viewLocationContext.ModulePath.TrimStart(new[] { '/' }); return string.Concat(path, "/", viewLocationContext.ModuleName, "/", viewName, "-", viewLocationContext.Context.Culture); }, // 1 Handles: *modulepath* / *modulename* / *viewname* (viewName, model, viewLocationContext) =>{ if (string.IsNullOrEmpty(viewLocationContext.ModulePath)) { return string.Empty; } var path = viewLocationContext.ModulePath.TrimStart(new[] { '/' }); return string.Concat(path, "/", viewLocationContext.ModuleName, "/", viewName); }, (viewName, model, viewLocationContext) =>{ return string.IsNullOrEmpty(viewLocationContext.ModulePath) ? string.Empty : string.Concat("views/", viewLocationContext.ModulePath.TrimStart(new[] { '/' }), "/", viewName, "-", viewLocationContext.Context.Culture); }, // 2 Handles: views / *modulepath* / *viewname* (viewName, model, viewLocationContext) =>{ return string.IsNullOrEmpty(viewLocationContext.ModulePath) ? string.Empty : string.Concat("views/", viewLocationContext.ModulePath.TrimStart(new[] {'/'}), "/", viewName); }, (viewName, model, viewLocationContext) =>{ return string.IsNullOrEmpty(viewLocationContext.ModulePath) ? string.Empty : string.Concat(viewLocationContext.ModulePath.TrimStart(new[] { '/' }), "/", viewName, "-", viewLocationContext.Context.Culture); }, // 3 Handles: *modulepath* / *viewname* (viewName, model, viewLocationContext) =>{ return string.IsNullOrEmpty(viewLocationContext.ModulePath) ? string.Empty : string.Concat(viewLocationContext.ModulePath.TrimStart(new[] { '/' }), "/", viewName); }, (viewName, model, viewLocationContext) => { return string.Concat("views/", viewLocationContext.ModuleName, "/", viewName, "-", viewLocationContext.Context.Culture); }, // 4 Handles: views / *modulename* / *viewname* (viewName, model, viewLocationContext) => { return string.Concat("views/", viewLocationContext.ModuleName, "/", viewName); }, (viewName, model, viewLocationContext) => { return string.Concat(viewLocationContext.ModuleName, "/", viewName, "-", viewLocationContext.Context.Culture); }, // 5 Handles: *modulename* / *viewname* (viewName, model, viewLocationContext) => { return string.Concat(viewLocationContext.ModuleName, "/", viewName); }, (viewName, model, viewLocationContext) => { return string.Concat("views/", viewName, "-", viewLocationContext.Context.Culture); }, // 6 Handles: views / *viewname* (viewName, model, viewLocationContext) => { return string.Concat("views/", viewName); }, (viewName, model, viewLocationContext) => { return string.Concat(viewName, "-", viewLocationContext.Context.Culture); }, // 7 Handles: *viewname* (viewName, model, viewLocationContext) => { return viewName; } }; } } } ================================================ FILE: src/Nancy/Conventions/IConvention.cs ================================================ namespace Nancy.Conventions { using System; /// /// Provides Nancy convention defaults and validation /// public interface IConvention { /// /// Initialise any conventions this class "owns" /// /// Convention object instance void Initialise(NancyConventions conventions); /// /// Asserts that the conventions that this class "owns" are valid /// /// Conventions object instance /// Tuple containing true/false for valid/invalid, and any error messages Tuple Validate(NancyConventions conventions); } } ================================================ FILE: src/Nancy/Conventions/NancyConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using Nancy.Bootstrapper; using Nancy.ViewEngines; /// /// Nancy configurable conventions /// public class NancyConventions { private readonly ITypeCatalog typeCatalog; private IEnumerable conventions; /// /// Initializes a new instance of the class. /// public NancyConventions(ITypeCatalog typeCatalog) { this.typeCatalog = typeCatalog; this.BuildDefaultConventions(); } /// /// Gets or sets the conventions for locating view templates /// public IList> ViewLocationConventions { get; set; } /// /// Gets or sets the conventions for locating and serving static content /// public IList> StaticContentsConventions { get; set; } /// /// Gets or sets the conventions for coercing accept headers from their source /// values to the real values for content negotiation /// /// public IList>, NancyContext, IEnumerable>>> AcceptHeaderCoercionConventions { get; set; } /// /// Gets or sets the conventions for determining request culture /// public IList> CultureConventions { get; set; } /// /// Validates the conventions /// /// A tuple containing a flag indicating validity, and any error messages public Tuple Validate() { var isValid = true; var errorMessageBuilder = new StringBuilder(); foreach (var result in this.conventions.Select(convention => convention.Validate(this)).Where(result => !result.Item1)) { isValid = false; errorMessageBuilder.AppendLine(result.Item2); } return new Tuple(isValid, errorMessageBuilder.ToString()); } /// /// Gets the instance registrations for registering into the container /// /// Enumeration of InstanceRegistration types public IEnumerable GetInstanceRegistrations() { return new[] { new InstanceRegistration(typeof(ViewLocationConventions), new ViewLocationConventions(this.ViewLocationConventions)), new InstanceRegistration(typeof(StaticContentsConventions), new StaticContentsConventions(this.StaticContentsConventions)), new InstanceRegistration(typeof(AcceptHeaderCoercionConventions), new AcceptHeaderCoercionConventions(this.AcceptHeaderCoercionConventions)), new InstanceRegistration(typeof(CultureConventions), new CultureConventions(this.CultureConventions)) }; } /// /// Locates all the default conventions and calls them in /// turn to build up the default conventions. /// private void BuildDefaultConventions() { var defaultConventions = this.typeCatalog.GetTypesAssignableTo(TypeResolveStrategies.OnlyNancy); this.conventions = defaultConventions .Union(this.typeCatalog.GetTypesAssignableTo(TypeResolveStrategies.ExcludeNancy)) .Select(t => (IConvention)Activator.CreateInstance(t)); foreach (var convention in this.conventions) { convention.Initialise(this); } } } } ================================================ FILE: src/Nancy/Conventions/StaticContentConventionBuilder.cs ================================================ using Nancy.Diagnostics; namespace Nancy.Conventions { using System; using System.Collections.Concurrent; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Nancy.Helpers; using Nancy.Responses; /// /// Helper class for defining directory-based conventions for static contents. /// public class StaticContentConventionBuilder { private static readonly ConcurrentDictionary> ResponseFactoryCache; private static readonly Regex PathReplaceRegex = new Regex(@"[/\\]", RegexOptions.Compiled); static StaticContentConventionBuilder() { ResponseFactoryCache = new ConcurrentDictionary>(); } /// /// Adds a directory-based convention for static convention. /// /// The path that should be matched with the request. /// The path to where the content is stored in your application, relative to the root. If this is then it will be the same as . /// A list of extensions that is valid for the conventions. If not supplied, all extensions are valid. /// A instance for the requested static contents if it was found, otherwise . public static Func AddDirectory(string requestedPath, string contentPath = null, params string[] allowedExtensions) { if (!requestedPath.StartsWith("/")) { requestedPath = string.Concat("/", requestedPath); } return (ctx, root) => { var path = HttpUtility.UrlDecode(ctx.Request.Path); var fileName = GetSafeFileName(path); if (string.IsNullOrEmpty(fileName)) { return null; } var pathWithoutFilename = GetPathWithoutFilename(fileName, path); if (!pathWithoutFilename.StartsWith(requestedPath, StringComparison.OrdinalIgnoreCase)) { (ctx.Trace.TraceLog ?? new NullLog()).WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The requested resource '", path, "' does not match convention mapped to '", requestedPath, "'" ))); return null; } contentPath = GetContentPath(requestedPath, contentPath); if (contentPath.Equals("/")) { throw new ArgumentException("This is not the security vulnerability you are looking for. Mapping static content to the root of your application is not a good idea."); } var responseFactory = ResponseFactoryCache.GetOrAdd(new ResponseFactoryCacheKey(path, root), BuildContentDelegate(ctx, root, requestedPath, contentPath, allowedExtensions)); return responseFactory.Invoke(ctx); }; } /// /// Adds a file-based convention for static convention. /// /// The file that should be matched with the request. /// The file that should be served when the requested path is matched. public static Func AddFile(string requestedFile, string contentFile) { return (ctx, root) => { var path = ctx.Request.Path; if (!path.Equals(requestedFile, StringComparison.OrdinalIgnoreCase)) { ctx.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The requested resource '", path, "' does not match convention mapped to '", requestedFile, "'"))); return null; } var responseFactory = ResponseFactoryCache.GetOrAdd(new ResponseFactoryCacheKey(path, root), BuildContentDelegate(ctx, root, requestedFile, contentFile, ArrayCache.Empty())); return responseFactory.Invoke(ctx); }; } private static string GetSafeFileName(string path) { try { return Path.GetFileName(path); } catch (Exception) { } return null; } private static string GetSafeFullPath(string path) { try { return Path.GetFullPath(path); } catch (Exception) { } return null; } private static string GetContentPath(string requestedPath, string contentPath) { contentPath = contentPath ?? requestedPath; if (!contentPath.StartsWith("/")) { contentPath = string.Concat("/", contentPath); } return contentPath; } private static Func> BuildContentDelegate(NancyContext context, string applicationRootPath, string requestedPath, string contentPath, string[] allowedExtensions) { return pathAndRootPair => { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] Attempting to resolve static content '", pathAndRootPair, "'"))); var extension = Path.GetExtension(pathAndRootPair.Path); if (!string.IsNullOrEmpty(extension)) { extension = extension.Substring(1); } if (allowedExtensions.Length != 0 && !allowedExtensions.Any(e => string.Equals(e.TrimStart(new [] {'.'}), extension, StringComparison.OrdinalIgnoreCase))) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The requested extension '", extension, "' does not match any of the valid extensions for the convention '", string.Join(",", allowedExtensions), "'"))); return ctx => null; } var transformedRequestPath = GetSafeRequestPath(pathAndRootPair.Path, requestedPath, contentPath); transformedRequestPath = GetEncodedPath(transformedRequestPath); var relativeFileName = Path.Combine(applicationRootPath, transformedRequestPath); var fileName = GetSafeFullPath(relativeFileName); if (fileName == null) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The request '", relativeFileName, "' contains an invalid path character"))); return ctx => null; } var relatveContentRootPath = Path.Combine(applicationRootPath, GetEncodedPath(contentPath)); var contentRootPath = GetSafeFullPath(relatveContentRootPath); if (contentRootPath == null) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The request '", fileName, "' is trying to access a path inside the content folder, which contains an invalid path character '", relatveContentRootPath, "'"))); return ctx => null; } if (!IsWithinContentFolder(contentRootPath, fileName)) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The request '", fileName, "' is trying to access a path outside the content folder '", contentPath, "'"))); return ctx => null; } if (!File.Exists(fileName)) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] The requested file '", fileName, "' does not exist"))); return ctx => null; } context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[StaticContentConventionBuilder] Returning file '", fileName, "'"))); return ctx => new GenericFileResponse(fileName, ctx); }; } private static string GetEncodedPath(string path) { return PathReplaceRegex.Replace(path.TrimStart(new[] { '/' }), Path.DirectorySeparatorChar.ToString()); } private static string GetPathWithoutFilename(string fileName, string path) { var pathWithoutFileName = path.Replace(fileName, string.Empty); return (pathWithoutFileName.Equals("/")) ? pathWithoutFileName : pathWithoutFileName.TrimEnd(new[] {'/'}); } private static string GetSafeRequestPath(string requestPath, string requestedPath, string contentPath) { var actualContentPath = (contentPath.Equals("/") ? string.Empty : contentPath); if (requestedPath.Equals("/")) { return string.Concat(actualContentPath, requestPath); } var expression = new Regex(Regex.Escape(requestedPath), RegexOptions.IgnoreCase); return expression.Replace(requestPath, actualContentPath, 1); } /// /// Returns whether the given filename is contained within the content folder /// /// Content root path /// Filename requested /// True if contained within the content root, false otherwise private static bool IsWithinContentFolder(string contentRootPath, string fileName) { return fileName.StartsWith(contentRootPath, StringComparison.Ordinal); } /// /// Used to uniquely identify a request. Needed for when two Nancy applications want to serve up static content of the same /// name from within the same AppDomain. /// private class ResponseFactoryCacheKey : IEquatable { private readonly string path; private readonly string rootPath; public ResponseFactoryCacheKey(string path, string rootPath) { this.path = path; this.rootPath = rootPath; } /// /// The path of the static content for which this response is being issued /// public string Path { get { return this.path; } } /// /// The root folder path of the Nancy application for which this response will be issued /// public string RootPath { get { return this.rootPath; } } public bool Equals(ResponseFactoryCacheKey other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return string.Equals(this.path, other.path) && string.Equals(this.rootPath, other.rootPath); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != this.GetType()) { return false; } return Equals((ResponseFactoryCacheKey)obj); } public override int GetHashCode() { unchecked { return ((this.path != null ? this.path.GetHashCode() : 0) * 397) ^ (this.rootPath != null ? this.rootPath.GetHashCode() : 0); } } } } } ================================================ FILE: src/Nancy/Conventions/StaticContentHelper.cs ================================================ namespace Nancy.Conventions { using System; /// /// Nancy static convention helper /// public static class StaticContentHelper { /// /// Extension method for NancyConventions /// /// conventions.MapStaticContent((File, Directory) => /// { /// File["/page.js"] = "page.js"; /// Directory["/images"] = "images"; /// }); /// /// /// The conventions to add to. /// The callback method allows you to describe the static content public static void MapStaticContent(this NancyConventions conventions, Action staticConventions) { staticConventions(new StaticFileContent(conventions), new StaticDirectoryContent(conventions)); } } } ================================================ FILE: src/Nancy/Conventions/StaticContentsConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections; using System.Collections.Generic; /// /// Collection class for static content conventions /// public class StaticContentsConventions : IEnumerable> { private readonly IEnumerable> conventions; /// /// Initializes a new instance of the class, with /// the provided . /// /// The conventions. public StaticContentsConventions(IEnumerable> conventions) { this.conventions = conventions; } /// /// Returns an enumerator that iterates through the collection. /// /// /// An enumerator that can be used to iterate through the collection. /// public IEnumerator> GetEnumerator() { return conventions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: src/Nancy/Conventions/StaticContentsConventionsExtensions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections.Generic; /// /// Extension methods to adding static content conventions. /// public static class StaticContentsConventionsExtensions { /// /// Adds a directory-based convention for static convention. /// /// The conventions to add to. /// The path that should be matched with the request. /// The path to where the content is stored in your application, relative to the root. If this is then it will be the same as . /// A list of extensions that is valid for the conventions. If not supplied, all extensions are valid. public static void AddDirectory(this IList> conventions, string requestedPath, string contentPath = null, params string[] allowedExtensions) { conventions.Add(StaticContentConventionBuilder.AddDirectory(requestedPath, contentPath, allowedExtensions)); } /// /// Adds a directory-based convention for static convention. /// /// The conventions to add to. /// The file that should be matched with the request. /// The file that should be served when the requested path is matched. public static void AddFile(this IList> conventions, string requestedFile, string contentFile) { conventions.Add(StaticContentConventionBuilder.AddFile(requestedFile, contentFile)); } } } ================================================ FILE: src/Nancy/Conventions/StaticDirectoryContent.cs ================================================ namespace Nancy.Conventions { /// /// Nancy static directory convention helper /// public class StaticDirectoryContent { private readonly NancyConventions conventions; /// /// Creates a new instance of StaticDirectoryContent /// /// NancyConventions, to which static directories get added public StaticDirectoryContent(NancyConventions conventions) { this.conventions = conventions; } /// /// Adds a new static directory to the nancy conventions /// /// The route of the file /// A list of extensions that is valid for the conventions. If not supplied, all extensions are valid public string this[string requestDirectory, params string[] allowedExtensions] { set { this.conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory(requestDirectory, value, allowedExtensions)); } } } } ================================================ FILE: src/Nancy/Conventions/StaticFileContent.cs ================================================ namespace Nancy.Conventions { /// /// Nancy static file convention helper /// public class StaticFileContent { private readonly NancyConventions conventions; /// /// Creates a new instance of StaticFileContent /// /// NancyConventions, to which static files get added public StaticFileContent(NancyConventions conventions) { this.conventions = conventions; } /// /// Adds a new static file to the nancy conventions /// /// The route of the file public string this[string requestFile] { set { this.conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddFile(requestFile, value)); } } } } ================================================ FILE: src/Nancy/Conventions/ViewLocationConventions.cs ================================================ namespace Nancy.Conventions { using System; using System.Collections; using System.Collections.Generic; using Nancy.ViewEngines; /// /// This is a wrapper around the type /// IEnumerable<Func<string, object, ViewLocationContext, string>> and its /// only purpose is to make Ninject happy which was throwing an exception /// when constructor injecting this type. /// public class ViewLocationConventions : IEnumerable> { private readonly IEnumerable> conventions; /// /// Initializes a new instance of the class, with /// the provided . /// /// The conventions. public ViewLocationConventions(IEnumerable> conventions) { this.conventions = conventions; } /// /// Returns an enumerator that iterates through the collection. /// /// /// An enumerator that can be used to iterate through the collection. /// public IEnumerator> GetEnumerator() { return conventions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: src/Nancy/Cookies/INancyCookie.cs ================================================ namespace Nancy.Cookies { using System; /// /// Defines the functionality of a Nancy context /// public interface INancyCookie { /// /// The domain to restrict the cookie to /// string Domain { get; set; } /// /// When the cookie should expire /// /// A instance containing the date and time when the cookie should expire; otherwise if it should expire at the end of the session. DateTime? Expires { get; set; } /// /// The name of the cookie /// string Name { get; } /// /// Gets the encoded name of the cookie /// string EncodedName { get; } /// /// The path to restrict the cookie to /// string Path { get; set; } /// /// The value of the cookie /// string Value { get; } /// /// Gets the encoded value of the cookie /// string EncodedValue { get; } /// /// Whether the cookie is http only /// bool HttpOnly { get; } /// /// Whether the cookie is secure (i.e. HTTPS only) /// bool Secure { get; } } } ================================================ FILE: src/Nancy/Cookies/NancyCookie.cs ================================================ namespace Nancy.Cookies { using System; using System.Globalization; using System.Text; using Nancy.Helpers; /// /// Default cookie implementation for Nancy. /// public class NancyCookie : INancyCookie { /// /// Initializes a new instance of the class, with /// the provided and . /// /// The name of the cookie. /// The value of the cookie. public NancyCookie(string name, string value) : this(name, value, false) { } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The name of the cookie. /// The value of the cookie. /// The expiration date of the cookie. Can be if it should expire at the end of the session. public NancyCookie(string name, string value, DateTime expires) : this(name, value, false, false, expires) { } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The name of the cookie. /// The value of the cookie. /// Whether the cookie is http only. public NancyCookie(string name, string value, bool httpOnly) : this(name, value, httpOnly, false, null) { } /// /// Initializes a new instance of the class, with /// the provided , , and . /// /// The name of the cookie. /// The value of the cookie. /// Whether the cookie is http only. /// Whether the cookie is secure (i.e. HTTPS only). public NancyCookie(string name, string value, bool httpOnly, bool secure) : this(name, value, httpOnly, secure, null) { } /// /// Initializes a new instance of the class, with /// the provided , , , and . /// /// The name of the cookie. /// The value of the cookie. /// Whether the cookie is http only. /// Whether the cookie is secure (i.e. HTTPS only). /// The expiration date of the cookie. Can be if it should expire at the end of the session. public NancyCookie(string name, string value, bool httpOnly, bool secure, DateTime? expires) { this.Name = name; this.Value = value; this.HttpOnly = httpOnly; this.Secure = secure; this.Expires = expires; } /// /// The domain to restrict the cookie to /// public string Domain { get; set; } /// /// When the cookie should expire /// /// A instance containing the date and time when the cookie should expire; otherwise if it should expire at the end of the session. public DateTime? Expires { get; set; } /// /// The name of the cookie /// public string Name { get; private set; } /// /// Gets the encoded name of the cookie /// public string EncodedName { get { return HttpUtility.UrlEncode(this.Name); } } /// /// The path to restrict the cookie to /// public string Path { get; set; } /// /// The value of the cookie /// public string Value { get; private set; } /// /// Gets the encoded value of the cookie /// public string EncodedValue { get { return HttpUtility.UrlEncode(this.Value); } } /// /// Whether the cookie is http only /// public bool HttpOnly { get; private set; } /// /// Whether the cookie is secure (i.e. HTTPS only) /// public bool Secure { get; private set; } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { var sb = new StringBuilder(50); sb.AppendFormat("{0}={1}; path={2}", this.EncodedName, this.EncodedValue, Path ?? "/"); if (Expires != null) { sb.Append("; expires="); sb.Append(Expires.Value.ToUniversalTime().ToString("ddd, dd-MMM-yyyy HH:mm:ss", DateTimeFormatInfo.InvariantInfo)); sb.Append(" GMT"); } if (Domain != null) { sb.Append("; domain="); sb.Append(Domain); } if (Secure) { sb.Append("; Secure"); } if (HttpOnly) { sb.Append("; HttpOnly"); } return sb.ToString(); } } } ================================================ FILE: src/Nancy/Cryptography/AesEncryptionProvider.cs ================================================ namespace Nancy.Cryptography { using System; using System.Security.Cryptography; using System.Text; /// /// Default encryption provider using Aes /// public class AesEncryptionProvider : IEncryptionProvider { private readonly byte[] key; private readonly byte[] iv; /// /// Creates a new instance of the AesEncryptionProvider class /// /// Key generator to use to generate the key and iv public AesEncryptionProvider(IKeyGenerator keyGenerator) { this.key = keyGenerator.GetBytes(32); this.iv = keyGenerator.GetBytes(16); } /// /// Encrypt data /// /// Data to encrypt /// Encrypted string public string Encrypt(string data) { using (var provider = Aes.Create()) using (var encryptor = provider.CreateEncryptor(this.key, this.iv)) { var input = Encoding.UTF8.GetBytes(data); var output = encryptor.TransformFinalBlock(input, 0, input.Length); return Convert.ToBase64String(output); } } /// /// Decrypt string /// /// Data to decrypt /// Decrypted string public string Decrypt(string data) { try { using (var provider = Aes.Create()) using (var decryptor = provider.CreateDecryptor(this.key, this.iv)) { var input = Convert.FromBase64String(data); var output = decryptor.TransformFinalBlock(input, 0, input.Length); return Encoding.UTF8.GetString(output); } } catch (FormatException) { return String.Empty; } catch (CryptographicException) { return String.Empty; } catch(ArgumentException ex) { if (ex.ParamName == null) { return String.Empty; } throw; } } } } ================================================ FILE: src/Nancy/Cryptography/Base64Helpers.cs ================================================ namespace Nancy.Cryptography { using System; /// /// Helper class for base64 encoding related tasks. /// public static class Base64Helpers { /// /// Calculates how long a byte array of X length will be after base64 encoding /// /// The normal, 8bit per byte, length of the byte array /// Base64 encoded length public static int GetBase64Length(int normalLength) { var inputPadding = (normalLength % 3 != 0) ? (3 - (normalLength % 3)) : 0; return (int)Math.Ceiling((normalLength + inputPadding) * 4.0 / 3.0); } } } ================================================ FILE: src/Nancy/Cryptography/CryptographyConfiguration.cs ================================================ namespace Nancy.Cryptography { using System; /// /// Cryptographic setup for classes that use encryption and HMAC /// public class CryptographyConfiguration { private static readonly Lazy DefaultConfiguration = new Lazy(() => new CryptographyConfiguration( new AesEncryptionProvider(new RandomKeyGenerator()), new DefaultHmacProvider(new RandomKeyGenerator()))); private static readonly Lazy NoEncryptionConfiguration = new Lazy(() => new CryptographyConfiguration( new NoEncryptionProvider(), new DefaultHmacProvider(new RandomKeyGenerator()))); /// /// Creates a new instance of the CryptographyConfiguration class /// /// Encryption provider /// HMAC provider public CryptographyConfiguration(IEncryptionProvider encryptionProvider, IHmacProvider hmacProvider) { this.EncryptionProvider = encryptionProvider; this.HmacProvider = hmacProvider; } /// /// Gets the default configuration - Rijndael encryption, HMACSHA256 HMAC, random keys /// public static CryptographyConfiguration Default { get { return DefaultConfiguration.Value; } } /// /// Gets configuration with no encryption and HMACSHA256 HMAC with a random key /// public static CryptographyConfiguration NoEncryption { get { return NoEncryptionConfiguration.Value; } } /// /// Gets the encryption provider /// public IEncryptionProvider EncryptionProvider { get; private set; } /// /// Gets the hmac provider /// public IHmacProvider HmacProvider { get; private set; } } } ================================================ FILE: src/Nancy/Cryptography/DefaultHmacProvider.cs ================================================ namespace Nancy.Cryptography { using System.Security.Cryptography; using System.Text; /// /// Provides SHA-256 HMACs /// public class DefaultHmacProvider : IHmacProvider { /// /// HMAC length /// private readonly int hmacLength = new HMACSHA256().HashSize / 8; /// /// Preferred key size for HMACSHA256 /// private const int PreferredKeySize = 64; /// /// Key /// private readonly byte[] key; /// /// Creates a new instance of the DefaultHmacProvider type /// /// Key generator to use to generate the key public DefaultHmacProvider(IKeyGenerator keyGenerator) { this.key = keyGenerator.GetBytes(PreferredKeySize); } /// /// Gets the length of the HMAC signature /// public int HmacLength { get { return this.hmacLength; } } /// /// Create a hmac from the given data using the given passPhrase /// /// Data to create hmac from /// String representation of the hmac public byte[] GenerateHmac(string data) { return this.GenerateHmac(Encoding.UTF8.GetBytes(data)); } /// /// Create a hmac from the given data /// /// Data to create hmac from /// Hmac bytes public byte[] GenerateHmac(byte[] data) { using (var hmacGenerator = new HMACSHA256(this.key)) { return hmacGenerator.ComputeHash(data); } } } } ================================================ FILE: src/Nancy/Cryptography/HmacComparer.cs ================================================ namespace Nancy.Cryptography { using System; using System.Runtime.CompilerServices; /// /// Compares two HMAC /// public static class HmacComparer { /// /// Compare two hmac byte arrays without any early exits /// /// First hmac /// Second hmac /// Expected length of the hash /// True if equal, false otherwise [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static bool Compare(byte[] hmac1, byte[] hmac2, int hashLength) { var hasResized = false; if (hmac1.Length != hashLength) { Array.Resize(ref hmac1, hashLength); hasResized = true; } if (hmac2.Length != hashLength) { Array.Resize(ref hmac2, hashLength); hasResized = true; } var isValid = true; for (int i = 0; i < hashLength; i++) { if (hmac1[i] != hmac2[i]) { isValid = false; } } return hasResized ? false : isValid; } } } ================================================ FILE: src/Nancy/Cryptography/IEncryptionProvider.cs ================================================ namespace Nancy.Cryptography { /// /// Provides symmetrical encryption support /// public interface IEncryptionProvider { /// /// Encrypt and base64 encode the string /// /// Data to encrypt /// Encrypted string string Encrypt(string data); /// /// Decrypt string /// /// Data to decrypt /// Decrypted string string Decrypt(string data); } } ================================================ FILE: src/Nancy/Cryptography/IHmacProvider.cs ================================================ namespace Nancy.Cryptography { /// /// Creates Hash-based Message Authentication Codes (HMACs) /// public interface IHmacProvider { /// /// Gets the length of the HMAC signature in bytes /// int HmacLength { get; } /// /// Create a hmac from the given data /// /// Data to create hmac from /// Hmac bytes byte[] GenerateHmac(string data); /// /// Create a hmac from the given data /// /// Data to create hmac from /// Hmac bytes byte[] GenerateHmac(byte[] data); } } ================================================ FILE: src/Nancy/Cryptography/IKeyGenerator.cs ================================================ namespace Nancy.Cryptography { /// /// Provides key byte generation /// public interface IKeyGenerator { /// /// Generate a sequence of bytes /// /// Number of bytes to return /// Array bytes byte[] GetBytes(int count); } } ================================================ FILE: src/Nancy/Cryptography/NoEncryptionProvider.cs ================================================ namespace Nancy.Cryptography { using System; using System.Text; /// /// A "no op" encryption provider /// Useful for debugging or performance. /// public class NoEncryptionProvider : IEncryptionProvider { /// /// Encrypt data /// /// Data to encrypt /// Encrypted string public string Encrypt(string data) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(data)); } /// /// Decrypt string /// /// Data to decrypt /// Decrypted string public string Decrypt(string data) { return Encoding.UTF8.GetString(Convert.FromBase64String(data)); } } } ================================================ FILE: src/Nancy/Cryptography/PassphraseKeyGenerator.cs ================================================ namespace Nancy.Cryptography { using System; using System.Security.Cryptography; /// /// Provides key generation using PBKDF2 / Rfc2898 /// NOTE: the salt is static so the passphrase should be long and complex /// (As the bytes are generated at app startup, because it's too slow to do per /// request, so the salt cannot be randomly generated and stored) /// public class PassphraseKeyGenerator : IKeyGenerator { private readonly Rfc2898DeriveBytes provider; /// /// Initializes a new instance of the class, with /// the provided , and optional /// number of /// /// The passphrase that should be used. /// The salt /// The number of iterations. The default value is 10000. public PassphraseKeyGenerator(string passphrase, byte[] salt, int iterations = 10000) { if (salt.Length < 8) { throw new ArgumentOutOfRangeException("salt", "salt must be at least 8 bytes in length"); } this.provider = new Rfc2898DeriveBytes(passphrase, salt, iterations); } /// /// Generate a sequence of bytes /// /// Number of bytes to return /// Array bytes public byte[] GetBytes(int count) { return provider.GetBytes(count); } } } ================================================ FILE: src/Nancy/Cryptography/RandomKeyGenerator.cs ================================================ namespace Nancy.Cryptography { using System.Security.Cryptography; /// /// Generates random secure keys using RandomNumberGenerator /// public class RandomKeyGenerator : IKeyGenerator { private readonly RandomNumberGenerator provider = RandomNumberGenerator.Create(); /// /// Generate a sequence of bytes /// /// Number of bytes to return /// /// Array bytes /// public byte[] GetBytes(int count) { var buffer = new byte[count]; this.provider.GetBytes(buffer); return buffer; } } } ================================================ FILE: src/Nancy/Culture/DefaultCultureService.cs ================================================ namespace Nancy.Culture { using System.Globalization; using Nancy.Configuration; using Nancy.Conventions; /// /// Determines current culture for context /// public class DefaultCultureService : ICultureService { private readonly CultureConventions cultureConventions; private readonly GlobalizationConfiguration configuration; /// /// Creates a new instance of DefaultCultureService /// /// CultureConventions to use for determining culture /// An instance of to retrieve from. public DefaultCultureService(CultureConventions cultureConventions, INancyEnvironment environment) { this.cultureConventions = cultureConventions; this.configuration = environment.GetValue(); } /// /// Determine current culture for NancyContext /// /// NancyContext /// CultureInfo public CultureInfo DetermineCurrentCulture(NancyContext context) { CultureInfo culture = null; foreach (var convention in this.cultureConventions) { culture = convention(context, this.configuration); if (culture != null) { break; } } return culture; } } } ================================================ FILE: src/Nancy/Culture/ICultureService.cs ================================================ namespace Nancy.Culture { using System.Globalization; /// /// Provides current culture for Nancy context /// public interface ICultureService { /// /// Determine current culture for NancyContext /// /// NancyContext /// CultureInfo CultureInfo DetermineCurrentCulture(NancyContext context); } } ================================================ FILE: src/Nancy/DefaultGlobalizationConfigurationProvider.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Provides the default . /// public class DefaultGlobalizationConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance public override GlobalizationConfiguration GetDefaultConfiguration() { return GlobalizationConfiguration.Default; } } } ================================================ FILE: src/Nancy/DefaultNancyBootstrapper.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Diagnostics; using Nancy.TinyIoc; using Extensions; /// /// TinyIoC bootstrapper - registers default route resolver and registers itself as /// INancyModuleCatalog for resolving modules but behavior can be overridden if required. /// public class DefaultNancyBootstrapper : NancyBootstrapperWithRequestContainerBase { /// /// Default assemblies that are ignored for autoregister /// public static IEnumerable> DefaultAutoRegisterIgnoredAssemblies = new Func[] { asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal), asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal), asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal), asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal), asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal), asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal), asm => asm.FullName.StartsWith("IronPython", StringComparison.Ordinal), asm => asm.FullName.StartsWith("IronRuby", StringComparison.Ordinal), asm => asm.FullName.StartsWith("xunit", StringComparison.Ordinal), asm => asm.FullName.StartsWith("Nancy.Testing", StringComparison.Ordinal), asm => asm.FullName.StartsWith("MonoDevelop.NUnit", StringComparison.Ordinal), asm => asm.FullName.StartsWith("SMDiagnostics", StringComparison.Ordinal), asm => asm.FullName.StartsWith("CppCodeProvider", StringComparison.Ordinal), asm => asm.FullName.StartsWith("WebDev.WebHost40", StringComparison.Ordinal), asm => asm.FullName.StartsWith("nunit", StringComparison.Ordinal), asm => asm.FullName.StartsWith("nCrunch", StringComparison.Ordinal), }; /// /// Gets the assemblies to ignore when autoregistering the application container /// Return true from the delegate to ignore that particular assembly, returning false /// does not mean the assembly *will* be included, a true from another delegate will /// take precedence. /// protected virtual IEnumerable> AutoRegisterIgnoredAssemblies { get { return DefaultAutoRegisterIgnoredAssemblies; } } /// /// Configures the container using AutoRegister followed by registration /// of default INancyModuleCatalog and IRouteResolver. /// /// Container instance protected override void ConfigureApplicationContainer(TinyIoCContainer container) { AutoRegister(container, this.AutoRegisterIgnoredAssemblies); } /// /// Resolve INancyEngine /// /// INancyEngine implementation protected override sealed INancyEngine GetEngineInternal() { return this.ApplicationContainer.Resolve(); } /// /// Create a default, unconfigured, container /// /// Container instance protected override TinyIoCContainer GetApplicationContainer() { return new TinyIoCContainer(); } /// /// Registers an instance in the container. /// /// The container to register into. /// The instance to register. protected override void RegisterNancyEnvironment(TinyIoCContainer container, INancyEnvironment environment) { container.Register(environment); } /// /// Register the bootstrapper's implemented types into the container. /// This is necessary so a user can pass in a populated container but not have /// to take the responsibility of registering things like INancyModuleCatalog manually. /// /// Application container to register into protected override sealed void RegisterBootstrapperTypes(TinyIoCContainer applicationContainer) { applicationContainer.Register(this); } /// /// Register the default implementations of internally used types into the container as singletons /// /// Container to register into /// Type registrations to register protected override sealed void RegisterTypes(TinyIoCContainer container, IEnumerable typeRegistrations) { foreach (var typeRegistration in typeRegistrations) { switch (typeRegistration.Lifetime) { case Lifetime.Transient: container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsMultiInstance(); break; case Lifetime.Singleton: container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsSingleton(); break; case Lifetime.PerRequest: throw new InvalidOperationException("Unable to directly register a per request lifetime."); default: throw new ArgumentOutOfRangeException(); } } } /// /// Register the various collections into the container as singletons to later be resolved /// by IEnumerable{Type} constructor dependencies. /// /// Container to register into /// Collection type registrations to register protected override sealed void RegisterCollectionTypes(TinyIoCContainer container, IEnumerable collectionTypeRegistrations) { foreach (var collectionTypeRegistration in collectionTypeRegistrations) { switch (collectionTypeRegistration.Lifetime) { case Lifetime.Transient: container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes).AsMultiInstance(); break; case Lifetime.Singleton: container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes).AsSingleton(); break; case Lifetime.PerRequest: throw new InvalidOperationException("Unable to directly register a per request lifetime."); default: throw new ArgumentOutOfRangeException(); } } } /// /// Register the given module types into the container /// /// Container to register into /// NancyModule types protected override sealed void RegisterRequestContainerModules(TinyIoCContainer container, IEnumerable moduleRegistrationTypes) { foreach (var moduleRegistrationType in moduleRegistrationTypes) { container.Register( typeof(INancyModule), moduleRegistrationType.ModuleType, moduleRegistrationType.ModuleType.FullName). AsSingleton(); } } /// /// Register the given instances into the container /// /// Container to register into /// Instance registration types protected override void RegisterInstances(TinyIoCContainer container, IEnumerable instanceRegistrations) { foreach (var instanceRegistration in instanceRegistrations) { container.Register( instanceRegistration.RegistrationType, instanceRegistration.Implementation); } } /// /// Creates a per request child/nested container /// /// Current context /// Request container instance protected override TinyIoCContainer CreateRequestContainer(NancyContext context) { return this.ApplicationContainer.GetChildContainer(); } /// /// Gets the used by th. /// /// An instance. protected override INancyEnvironmentConfigurator GetEnvironmentConfigurator() { return this.ApplicationContainer.Resolve(); } /// /// Gets the diagnostics for initialization /// /// IDiagnostics implementation protected override IDiagnostics GetDiagnostics() { return this.ApplicationContainer.Resolve(); } /// /// Gets all registered startup tasks /// /// An instance containing instances. protected override IEnumerable GetApplicationStartupTasks() { return this.ApplicationContainer.ResolveAll(false); } /// /// Gets all registered request startup tasks /// /// An instance containing instances. protected override IEnumerable RegisterAndGetRequestStartupTasks(TinyIoCContainer container, Type[] requestStartupTypes) { container.RegisterMultiple(typeof(IRequestStartup), requestStartupTypes); return container.ResolveAll(false); } /// /// Gets all registered application registration tasks /// /// An instance containing instances. protected override IEnumerable GetRegistrationTasks() { return this.ApplicationContainer.ResolveAll(false); } /// /// Get the instance. /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. public override INancyEnvironment GetEnvironment() { return this.ApplicationContainer.Resolve(); } /// /// Retrieve all module instances from the container /// /// Container to use /// Collection of NancyModule instances protected override sealed IEnumerable GetAllModules(TinyIoCContainer container) { var nancyModules = container.ResolveAll(false); return nancyModules; } /// /// Retrieve a specific module instance from the container /// /// Container to use /// Type of the module /// NancyModule instance protected override sealed INancyModule GetModule(TinyIoCContainer container, Type moduleType) { container.Register(typeof(INancyModule), moduleType); return container.Resolve(); } /// /// Executes auto registration with the given container. /// /// Container instance /// List of ignored assemblies private void AutoRegister(TinyIoCContainer container, IEnumerable> ignoredAssemblies) { var assembly = typeof(NancyEngine).GetTypeInfo().Assembly; container.AutoRegister(this.AssemblyCatalog.GetAssemblies().Where(a => !ignoredAssemblies.Any(ia => ia(a))), DuplicateImplementationActions.RegisterMultiple, t => t.GetAssembly() != assembly); } } } ================================================ FILE: src/Nancy/DefaultNancyContextFactory.cs ================================================ namespace Nancy { using Nancy.Configuration; using Nancy.Culture; using Nancy.Diagnostics; using Nancy.Localization; /// /// Creates NancyContext instances /// public class DefaultNancyContextFactory : INancyContextFactory { private readonly ICultureService cultureService; private readonly IRequestTraceFactory requestTraceFactory; private readonly ITextResource textResource; private readonly INancyEnvironment environment; /// /// Creates a new instance of the class. /// /// An instance. /// An instance. /// An instance. /// An instance. public DefaultNancyContextFactory(ICultureService cultureService, IRequestTraceFactory requestTraceFactory, ITextResource textResource, INancyEnvironment environment) { this.cultureService = cultureService; this.requestTraceFactory = requestTraceFactory; this.textResource = textResource; this.environment = environment; } /// /// Create a new instance. /// /// A instance. public NancyContext Create(Request request) { var context = new NancyContext(); context.Trace = this.requestTraceFactory.Create(request); context.Request = request; context.Culture = this.cultureService.DetermineCurrentCulture(context); context.Text = new TextResourceFinder(this.textResource, context); context.Environment = this.environment; // Move this to DefaultRequestTrace. context.Trace.TraceLog.WriteLog(s => s.AppendLine("New Request Started")); return context; } } } ================================================ FILE: src/Nancy/DefaultObjectSerializer.cs ================================================ namespace Nancy { using Extensions; using System; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Text; /// /// Serializes/Deserializes objects for sessions /// public class DefaultObjectSerializer : IObjectSerializer { /// /// Serialize an object /// /// Source object /// Serialised object string public string Serialize(object sourceObject) { if (sourceObject == null) { return string.Empty; } dynamic serializedObject = (sourceObject is string) ? sourceObject : AddTypeInformation(sourceObject); var json = SimpleJson.SerializeObject(serializedObject); return Convert.ToBase64String(Encoding.UTF8.GetBytes(json)); } private static dynamic AddTypeInformation(object sourceObject) { var assemblyQualifiedName = sourceObject.GetType().GetTypeInfo().AssemblyQualifiedName; dynamic serializedObject = sourceObject.ToDynamic(); serializedObject.TypeObject = assemblyQualifiedName; return serializedObject; } /// /// Deserialize an object string /// /// Source object string /// Deserialized object public object Deserialize(string sourceString) { if (string.IsNullOrEmpty(sourceString)) { return null; } try { var inputBytes = Convert.FromBase64String(sourceString); var json = Encoding.UTF8.GetString(inputBytes); if (!ContainsTypeDescription(json)) { return SimpleJson.DeserializeObject(json); } dynamic serializedObject = SimpleJson.DeserializeObject(json); return SimpleJson.DeserializeObject(json, Type.GetType(serializedObject.TypeObject)); } catch (FormatException) { return null; } catch (SerializationException) { return null; } catch (IOException) { return null; } } private static bool ContainsTypeDescription(string json) { return json.Contains("TypeObject"); } } } ================================================ FILE: src/Nancy/DefaultResponseFormatter.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// The default implementation of the interface. /// public class DefaultResponseFormatter : IResponseFormatter { private readonly IRootPathProvider rootPathProvider; private readonly NancyContext context; private readonly ISerializerFactory serializerFactory; private readonly INancyEnvironment environment; /// /// Initializes a new instance of the class. /// /// The that should be used by the instance. /// The that should be used by the instance. /// An instance"/>. /// An instance. public DefaultResponseFormatter(IRootPathProvider rootPathProvider, NancyContext context, ISerializerFactory serializerFactory, INancyEnvironment environment) { this.rootPathProvider = rootPathProvider; this.context = context; this.serializerFactory = serializerFactory; this.environment = environment; } /// /// Gets all factory. /// public ISerializerFactory SerializerFactory { get { return this.serializerFactory; } } /// /// Gets the context for which the response is being formatted. /// /// A instance. public NancyContext Context { get { return this.context; } } /// /// Gets the . /// /// An instance. public INancyEnvironment Environment { get { return this.environment; } } /// /// Gets the root path of the application. /// /// A containing the root path. public string RootPath { get { return this.rootPathProvider.GetRootPath(); } } } } ================================================ FILE: src/Nancy/DefaultResponseFormatterFactory.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// The default implementation of the interface. /// public class DefaultResponseFormatterFactory : IResponseFormatterFactory { private readonly IRootPathProvider rootPathProvider; private readonly ISerializerFactory serializerFactory; private readonly INancyEnvironment environment; /// /// Initializes a new instance of the class. /// /// An instance. /// An instance. /// An instance. public DefaultResponseFormatterFactory(IRootPathProvider rootPathProvider, ISerializerFactory serializerFactory, INancyEnvironment environment) { this.rootPathProvider = rootPathProvider; this.serializerFactory = serializerFactory; this.environment = environment; } /// /// Creates a new instance. /// /// The instance that should be used by the response formatter. /// An instance. public IResponseFormatter Create(NancyContext context) { return new DefaultResponseFormatter(this.rootPathProvider, context, this.serializerFactory, this.environment); } } } ================================================ FILE: src/Nancy/DefaultRootPathProvider.cs ================================================ namespace Nancy { using System; /// /// Default implementation of . /// public class DefaultRootPathProvider : IRootPathProvider { /// /// Returns the root folder path of the current Nancy application. /// /// A containing the path of the root folder. public string GetRootPath() { #if CORE return Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath; #else return AppDomain.CurrentDomain.BaseDirectory; #endif } } } ================================================ FILE: src/Nancy/DefaultRouteConfigurationProvider.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Provides the default configuration for . /// public class DefaultRouteConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// Will return . public override RouteConfiguration GetDefaultConfiguration() { return RouteConfiguration.Default; } } } ================================================ FILE: src/Nancy/DefaultRuntimeEnvironmentInformation.cs ================================================ namespace Nancy { using System; using System.Diagnostics; using System.Linq; using System.Reflection; /// /// Default implementation of the interface. /// public class DefaultRuntimeEnvironmentInformation : IRuntimeEnvironmentInformation { private readonly Lazy isDebug; /// /// Initializes a new instance of the class. /// /// An instance. public DefaultRuntimeEnvironmentInformation(ITypeCatalog typeCatalog) { this.isDebug = new Lazy(() => GetDebugMode(typeCatalog)); } /// /// Gets a value indicating if the application is running in debug mode. /// /// if the application is running in debug mode, otherwise . public virtual bool IsDebug { get { return this.isDebug.Value; } } private static bool GetDebugMode(ITypeCatalog typeCatalog) { try { return Debugger.IsAttached; } catch { return false; } } } } ================================================ FILE: src/Nancy/DefaultSerializerFactory.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Extensions; using Nancy.Responses.Negotiation; /// /// Default implementation of the interface. /// /// This implementation will ignore the default implementations (those found in the Nancy assembly) unless no other match could be made. public class DefaultSerializerFactory : ISerializerFactory { private readonly IEnumerable serializers; /// /// Initializes a new instance of the class, /// with the provided . /// /// The implementations that should be available to the factory. public DefaultSerializerFactory(IEnumerable serializers) { this.serializers = serializers; } /// /// Gets the implementation that can serialize the provided . /// /// The to get a serializer for. /// An instance, or if not match was found. /// If more than one (not counting the default serializers) matched the provided media range. public ISerializer GetSerializer(MediaRange mediaRange) { var defaultSerializerForMediaRange = this.GetDefaultSerializerForMediaRange(mediaRange); var matches = this.serializers .Where(x => x != defaultSerializerForMediaRange) .Where(x => SafeCanSerialize(x, mediaRange)).ToArray(); if (matches.Length > 1) { throw new InvalidOperationException(GetErrorMessage(matches, mediaRange)); } return matches .Concat(new[] { defaultSerializerForMediaRange }) .FirstOrDefault(); } private ISerializer GetDefaultSerializerForMediaRange(MediaRange mediaRange) { try { return this.serializers .Where(x => x.GetType().GetTypeInfo().Assembly.Equals(typeof(INancyEngine).GetAssembly())) .SingleOrDefault(x => x.CanSerialize(mediaRange)); } catch { return null; } } private static string GetErrorMessage(IEnumerable matches, MediaRange mediaRange) { var details = string.Join("\n", matches.Select(x => string.Concat(" - ", x.GetType().FullName))); return string.Format("Multiple ISerializer implementations matched the '{0}' media range.\nThe following serializers matched \n\n{1}", mediaRange, details); } private static bool SafeCanSerialize(ISerializer serializer, MediaRange mediaRange) { try { return serializer.CanSerialize(mediaRange); } catch { return false; } } } } ================================================ FILE: src/Nancy/DefaultStaticContentConfigurationProvider.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Provides the default . /// public class DefaultStaticContentConfigurationProvider : NancyDefaultConfigurationProvider { private readonly IRootPathProvider rootPathProvider; /// /// Creates and instance of . /// /// Use to get the root path of the application public DefaultStaticContentConfigurationProvider(IRootPathProvider rootPathProvider) { this.rootPathProvider = rootPathProvider; } /// /// Gets the default configuration instance to register in the . /// /// The configuration instance. public override StaticContentConfiguration GetDefaultConfiguration() { return new StaticContentConfiguration(safePaths:new []{this.rootPathProvider.GetRootPath()}); } } } ================================================ FILE: src/Nancy/DefaultStaticContentProvider.cs ================================================ namespace Nancy { using Nancy.Conventions; /// /// The default static content provider that uses /// to determine what static content to serve. /// public class DefaultStaticContentProvider : IStaticContentProvider { private readonly IRootPathProvider rootPathProvider; private readonly StaticContentsConventions conventions; private string rootPath; /// /// Initializes a new instance of the class, using the /// provided and . /// /// The current root path provider. /// The static content conventions. public DefaultStaticContentProvider(IRootPathProvider rootPathProvider, StaticContentsConventions conventions) { this.rootPathProvider = rootPathProvider; this.rootPath = this.rootPathProvider.GetRootPath(); this.conventions = conventions; } /// /// Gets the static content response, if possible. /// /// Current context /// Response if serving content, null otherwise public Response GetContent(NancyContext context) { foreach (var convention in this.conventions) { var result = convention.Invoke(context, this.rootPath); if (result != null) { return result; } } return null; } } } ================================================ FILE: src/Nancy/DefaultTraceConfigurationProvider.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Provides the default configuration for . /// public class DefaultTraceConfigurationProvider : NancyDefaultConfigurationProvider { private readonly IRuntimeEnvironmentInformation runtimeEnvironmentInformation; /// /// Initializes a new instance of the class. /// public DefaultTraceConfigurationProvider(IRuntimeEnvironmentInformation runtimeEnvironmentInformation) { this.runtimeEnvironmentInformation = runtimeEnvironmentInformation; } /// /// Gets the default configuration instance to register in the . /// /// The configuration instance public override TraceConfiguration GetDefaultConfiguration() { var isDebugMode = this.runtimeEnvironmentInformation.IsDebug; return new TraceConfiguration( enabled: false, displayErrorTraces: isDebugMode); } } } ================================================ FILE: src/Nancy/DefaultTypeCatalog.cs ================================================ namespace Nancy { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Default implementation of the interface. /// public class DefaultTypeCatalog : ITypeCatalog { private readonly IAssemblyCatalog assemblyCatalog; private readonly ConcurrentDictionary> cache; /// /// Initializes a new instance of the class. /// /// An instanced, used to get the assemblies that types should be resolved from. public DefaultTypeCatalog(IAssemblyCatalog assemblyCatalog) { this.assemblyCatalog = assemblyCatalog; this.cache = new ConcurrentDictionary>(); } /// /// Gets all types that are assignable to the provided . /// /// The that returned types should be assignable to. /// A that should be used when retrieving types. /// An of instances. public IReadOnlyCollection GetTypesAssignableTo(Type type, TypeResolveStrategy strategy) { return this.cache.GetOrAdd(type, t => this.GetTypesAssignableTo(type)).Where(strategy.Invoke).ToArray(); } private IReadOnlyCollection GetTypesAssignableTo(Type type) { return this.assemblyCatalog .GetAssemblies() .SelectMany(assembly => assembly.SafeGetExportedTypes()) .Where(type.IsAssignableFrom) .Where(t => !t.GetTypeInfo().IsAbstract) .ToArray(); } } } ================================================ FILE: src/Nancy/DefaultViewConfigurationProvider.cs ================================================ namespace Nancy { using Configuration; /// /// Provides the default configuration for . /// public class DefaultViewConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance /// Will return . public override ViewConfiguration GetDefaultConfiguration() { return ViewConfiguration.Default;; } } } ================================================ FILE: src/Nancy/DependencyContextAssemblyCatalog.cs ================================================ #if CORE namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Microsoft.Extensions.DependencyModel; /// /// Default implementation of the interface, based on /// retrieving information from . /// public class DependencyContextAssemblyCatalog : IAssemblyCatalog { private static readonly Assembly NancyAssembly = typeof(INancyEngine).GetTypeInfo().Assembly; private readonly DependencyContext dependencyContext; /// /// Initializes a new instance of the class, /// using . /// public DependencyContextAssemblyCatalog() : this(Assembly.GetEntryAssembly()) { } /// /// Initializes a new instance of the class, /// using . /// public DependencyContextAssemblyCatalog(Assembly entryAssembly) { this.dependencyContext = DependencyContext.Load(entryAssembly); } /// /// Gets all instances in the catalog. /// /// An of instances. public virtual IReadOnlyCollection GetAssemblies() { var results = new HashSet { typeof (DependencyContextAssemblyCatalog).GetTypeInfo().Assembly }; foreach (var library in this.dependencyContext.RuntimeLibraries) { if (IsReferencingNancy(library)) { foreach (var assemblyName in library.GetDefaultAssemblyNames(this.dependencyContext)) { results.Add(SafeLoadAssembly(assemblyName)); } } } return results.ToArray(); } private static Assembly SafeLoadAssembly(AssemblyName assemblyName) { try { return Assembly.Load(assemblyName); } catch (Exception) { return null; } } private static bool IsReferencingNancy(Library library) { return library.Dependencies.Any(dependency => dependency.Name.Equals(NancyAssembly.GetName().Name)); } } } #endif ================================================ FILE: src/Nancy/Diagnostics/ConcurrentLimitedCollection.cs ================================================ namespace Nancy.Diagnostics { using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; /// /// Provides a thread safe, limited size, collection of objects /// If the collection is full the oldest item gets removed. /// /// Type to store public class ConcurrentLimitedCollection : IEnumerable { private readonly int maxSize; private ConcurrentQueue internalStore; /// /// Gets the current size for the collection. /// /// Current size of the collection. public int CurrentSize { get { return this.internalStore.Count; } } /// /// Initializes a new instance of the class, with /// the provided . /// /// The maximum size for the collection. public ConcurrentLimitedCollection(int maxSize) { this.maxSize = maxSize; this.internalStore = new ConcurrentQueue(); } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator GetEnumerator() { return this.internalStore.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// /// Adds an item to the collection. /// If the collection is full, the oldest item is removed and the new item /// is added to the end of the collection. /// /// Item to add public void Add(T item) { if (this.internalStore.Count == this.maxSize) { T temp; this.internalStore.TryDequeue(out temp); } this.internalStore.Enqueue(item); } /// /// Clear the collection /// public void Clear() { this.internalStore = new ConcurrentQueue(); } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultDiagnostics.cs ================================================ namespace Nancy.Diagnostics { using System.Collections.Generic; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Conventions; using Nancy.Culture; using Nancy.Localization; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Routing.Constraints; /// /// Wires up the diagnostics support at application startup. /// public class DefaultDiagnostics : IDiagnostics { private readonly IEnumerable diagnosticProviders; private readonly IRootPathProvider rootPathProvider; private readonly IRequestTracing requestTracing; private readonly NancyInternalConfiguration configuration; private readonly IModelBinderLocator modelBinderLocator; private readonly IEnumerable responseProcessors; private readonly IEnumerable routeSegmentConstraints; private readonly ICultureService cultureService; private readonly IRequestTraceFactory requestTraceFactory; private readonly IEnumerable routeMetadataProviders; private readonly ITextResource textResource; private readonly INancyEnvironment environment; private readonly ITypeCatalog typeCatalog; private readonly IAssemblyCatalog assemblyCatalog; private readonly AcceptHeaderCoercionConventions acceptHeaderCoercionConventions; /// /// Creates a new instance of the class. /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// public DefaultDiagnostics( IEnumerable diagnosticProviders, IRootPathProvider rootPathProvider, IRequestTracing requestTracing, NancyInternalConfiguration configuration, IModelBinderLocator modelBinderLocator, IEnumerable responseProcessors, IEnumerable routeSegmentConstraints, ICultureService cultureService, IRequestTraceFactory requestTraceFactory, IEnumerable routeMetadataProviders, ITextResource textResource, INancyEnvironment environment, ITypeCatalog typeCatalog, IAssemblyCatalog assemblyCatalog, AcceptHeaderCoercionConventions acceptHeaderCoercionConventions) { this.diagnosticProviders = diagnosticProviders; this.rootPathProvider = rootPathProvider; this.requestTracing = requestTracing; this.configuration = configuration; this.modelBinderLocator = modelBinderLocator; this.responseProcessors = responseProcessors; this.routeSegmentConstraints = routeSegmentConstraints; this.cultureService = cultureService; this.requestTraceFactory = requestTraceFactory; this.routeMetadataProviders = routeMetadataProviders; this.textResource = textResource; this.environment = environment; this.typeCatalog = typeCatalog; this.assemblyCatalog = assemblyCatalog; this.acceptHeaderCoercionConventions = acceptHeaderCoercionConventions; } /// /// Initialize diagnostics /// /// Application pipelines public void Initialize(IPipelines pipelines) { DiagnosticsHook.Enable( pipelines, this.diagnosticProviders, this.rootPathProvider, this.requestTracing, this.configuration, this.modelBinderLocator, this.responseProcessors, this.routeSegmentConstraints, this.cultureService, this.requestTraceFactory, this.routeMetadataProviders, this.textResource, this.environment, this.typeCatalog, this.assemblyCatalog, this.acceptHeaderCoercionConventions); } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultDiagnosticsConfigurationProvider.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Configuration; /// /// Provides the default configuration for . /// public class DefaultDiagnosticsConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance /// Will return public override DiagnosticsConfiguration GetDefaultConfiguration() { return DiagnosticsConfiguration.Default; } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultRequestTrace.cs ================================================ namespace Nancy.Diagnostics { using System.Collections.Generic; /// /// The default implementation of the interface. /// public class DefaultRequestTrace : IRequestTrace { /// /// Gets the generic item store. /// /// An instance containing the items. public IDictionary Items { get; set; } /// /// Gets or sets the information about the request. /// /// An instance. public RequestData RequestData { get; set; } /// /// Gets or sets the information about the response. /// /// An instance. public ResponseData ResponseData { get; set; } /// /// Gets or sets the trace log. /// /// A instance. public ITraceLog TraceLog { get; set; } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultRequestTraceFactory.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; using Nancy.Configuration; /// /// Default implementation of the interface. /// public class DefaultRequestTraceFactory : IRequestTraceFactory { private readonly TraceConfiguration configuration; /// /// Initializes a new instance of the class. /// /// An instance. public DefaultRequestTraceFactory(INancyEnvironment environment) { this.configuration = environment.GetValue(); } /// /// Creates an instance. /// /// A instance. /// An instance. public IRequestTrace Create(Request request) { var requestTrace = new DefaultRequestTrace(); var comparer = (StaticConfiguration.CaseSensitive) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; requestTrace.Items = new Dictionary(comparer); requestTrace.RequestData = request; requestTrace.TraceLog = (this.configuration.DisplayErrorTraces) ? (ITraceLog)new DefaultTraceLog() : (ITraceLog)new NullLog(); return requestTrace; } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultRequestTracing.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; using System.Linq; /// /// Default implementation of the interface. /// public class DefaultRequestTracing : IRequestTracing { private const int MaxSize = 50; private readonly ConcurrentLimitedCollection sessions = new ConcurrentLimitedCollection(MaxSize); /// /// Adds the , of the provided, to the trace log. /// /// The identifier of the trace. /// A instance. public void AddRequestDiagnosticToSession(Guid sessionId, NancyContext context) { var session = this.sessions.FirstOrDefault(s => s.Id == sessionId); if (session == null) { return; } session.AddRequestTrace(context.Trace); } /// /// Clears the trace log. /// public void Clear() { this.sessions.Clear(); } /// /// Creates a new trace session. /// /// A which represents the identifier of the new trace session. public Guid CreateSession() { var id = Guid.NewGuid(); this.sessions.Add(new RequestTraceSession(id)); return id; } // TODO - remove above method and return guid from here? /// /// Gets all the available instances. /// /// public IEnumerable GetSessions() { return this.sessions; } /// /// Checks if the provided is valid or not. /// /// A representing the session to check. /// if the session is valid, otherwise . public bool IsValidSessionId(Guid sessionId) { return this.sessions.Any(s => s.Id == sessionId); } } } ================================================ FILE: src/Nancy/Diagnostics/DefaultTraceLog.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Text; /// /// Default implementation of the interface. /// public class DefaultTraceLog : ITraceLog { private readonly StringBuilder log; /// /// Creates a new instance of the class. /// public DefaultTraceLog() { this.log = new StringBuilder(); } /// /// Write to the log /// /// Log writing delegate public void WriteLog(Action logDelegate) { if (this.log != null) { logDelegate.Invoke(this.log); } } /// /// Returns a string that represents the current object. /// /// /// A string that represents the current object. /// public override string ToString() { return this.log != null ? this.log.ToString() : string.Empty; } } } ================================================ FILE: src/Nancy/Diagnostics/DescriptionAttribute.cs ================================================ namespace Nancy.Diagnostics { using System; /// /// Attribute for specifying Method and Property descriptions. /// /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class DescriptionAttribute : Attribute { /// /// Gets or sets the description. /// /// The description. public string Description { get; set; } /// /// Initializes a new instance of the class. /// /// The description. public DescriptionAttribute(string description) { this.Description = description; } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticModule.cs ================================================ namespace Nancy.Diagnostics { using Configuration; /// /// Abstract base class for Nancy diagnostics module. /// /// public abstract class DiagnosticModule : NancyModule { private readonly INancyEnvironment environment; /// /// Initializes a new instance of the class. /// protected DiagnosticModule() : this(string.Empty) { } /// /// Initializes a new instance of the class, with /// the provided . /// /// The base path. protected DiagnosticModule(string basePath) : base(basePath) { this.environment = new DefaultNancyEnvironment(); this.environment.AddValue(ViewConfiguration.Default); } /// /// Renders a view from inside a route handler. /// /// A instance that is used to determine which view that should be rendered. public new DiagnosticsViewRenderer View { get { return new DiagnosticsViewRenderer(this.Context, this.environment); } } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsConfiguration.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Cryptography; /// /// Configuration for the diagnostics dashboard. /// public class DiagnosticsConfiguration { /// /// A default instance of the class. /// public static readonly DiagnosticsConfiguration Default = new DiagnosticsConfiguration { Enabled = false, CookieName = "__ncd", CryptographyConfiguration = CryptographyConfiguration.Default, Password = null, Path = "/_Nancy", SlidingTimeout = 15 }; private DiagnosticsConfiguration() { } /// /// Initializes a new instance of the class /// /// /// Password used to secure the dashboard. /// Relative path of the dashboard. /// Name of the cookie to store diagnostics information. /// Number of minutes that expiry of the diagnostics dashboard. /// Cryptography config to use for securing the dashboard. public DiagnosticsConfiguration(bool enabled, string password, string path, string cookieName, int slidingTimeout, CryptographyConfiguration cryptographyConfiguration) { this.Password = password ?? Default.Password; this.Path = GetNormalizedPath(path ?? Default.Path); this.CookieName = cookieName ?? Default.CookieName; this.Enabled = enabled; this.SlidingTimeout = slidingTimeout; this.CryptographyConfiguration = cryptographyConfiguration ?? Default.CryptographyConfiguration; } /// /// Gets or sets the name of the cookie used by the diagnostics dashboard. /// /// The default is __ncd public string CookieName { get; private set; } /// /// Gets the cryptography config to use for securing the diagnostics dashboard /// /// The default is public CryptographyConfiguration CryptographyConfiguration { get; private set; } /// /// Gets a value indicating if diagnostics is enabled or not. /// public bool Enabled { get; private set; } /// /// Gets password for accessing the diagnostics screen. /// /// The default value is . public string Password { get; private set; } /// /// Gets the path that the diagnostics dashboard will be accessible on. /// /// The default is /_Nancy. public string Path { get; private set; } /// /// The number of minutes that expiry of the diagnostics dashboard. Will be extended each time it is used. /// /// The default is 15 minutes. public int SlidingTimeout { get; private set; } private static string GetNormalizedPath(string path) { return (!path.StartsWith("/")) ? string.Concat("/", path) : path; } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsConfigurationExtensions.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Configuration; using Nancy.Cryptography; /// /// Contains configuration extensions for . /// public static class DiagnosticsConfigurationExtensions { /// /// Configures diagnostics. /// /// that should be configured. /// Password used to secure the dashboard. /// Relative path of the dashboard. /// Name of the cookie to store diagnostics information. /// Number of minutes that expiry of the diagnostics dashboard. /// Cryptography config to use for securing the dashboard. /// This will implicitly enable diagnostics. If you need control, please explicitly set enabled to either or . public static void Diagnostics(this INancyEnvironment environment, string password, string path = null, string cookieName = null, int slidingTimeout = 15, CryptographyConfiguration cryptographyConfiguration = null) { Diagnostics( environment, enabled: true, password: password, path: path, cookieName: cookieName, slidingTimeout: slidingTimeout, cryptographyConfiguration: cryptographyConfiguration); } /// /// Configures diagnostics. /// /// that should be configured. /// if diagnostics should be enabled, otherwise . /// Password used to secure the dashboard. /// Relative path of the dashboard. /// Name of the cookie to store diagnostics information. /// Number of minutes that expiry of the diagnostics dashboard. /// Cryptography config to use for securing the dashboard. public static void Diagnostics(this INancyEnvironment environment, bool enabled, string password, string path = null, string cookieName = null, int slidingTimeout = 15, CryptographyConfiguration cryptographyConfiguration = null) { environment.AddValue(new DiagnosticsConfiguration( enabled: enabled, password: password, path: path, cookieName: cookieName, slidingTimeout: slidingTimeout, cryptographyConfiguration: cryptographyConfiguration)); } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsHook.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Conventions; using Nancy.Cookies; using Nancy.Cryptography; using Nancy.Culture; using Nancy.Json; using Nancy.Localization; using Nancy.ModelBinding; using Nancy.Responses; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Routing.Constraints; using Nancy.Routing.Trie; /// /// Pipeline hook to handle diagnostics dashboard requests. /// public static class DiagnosticsHook { private static readonly CancellationToken CancellationToken = new CancellationToken(); private const string PipelineKey = "__Diagnostics"; internal const string ItemsKey = "DIAGS_REQUEST"; /// /// Enables the diagnostics dashboard and will intercept all requests that are passed to /// the condigured paths. /// public static void Enable(IPipelines pipelines, IEnumerable providers, IRootPathProvider rootPathProvider, IRequestTracing requestTracing, NancyInternalConfiguration configuration, IModelBinderLocator modelBinderLocator, IEnumerable responseProcessors, IEnumerable routeSegmentConstraints, ICultureService cultureService, IRequestTraceFactory requestTraceFactory, IEnumerable routeMetadataProviders, ITextResource textResource, INancyEnvironment environment, ITypeCatalog typeCatalog, IAssemblyCatalog assemblyCatalog, AcceptHeaderCoercionConventions acceptHeaderCoercionConventions) { var diagnosticsConfiguration = environment.GetValue(); var diagnosticsEnvironment = GetDiagnosticsEnvironment(); var diagnosticsModuleCatalog = new DiagnosticsModuleCatalog(providers, rootPathProvider, requestTracing, configuration, diagnosticsEnvironment, typeCatalog, assemblyCatalog); var diagnosticsRouteCache = new RouteCache( diagnosticsModuleCatalog, new DefaultNancyContextFactory(cultureService, requestTraceFactory, textResource, environment), new DefaultRouteSegmentExtractor(), new DefaultRouteDescriptionProvider(), cultureService, routeMetadataProviders); var diagnosticsRouteResolver = new DefaultRouteResolver( diagnosticsModuleCatalog, new DiagnosticsModuleBuilder(rootPathProvider, modelBinderLocator, diagnosticsEnvironment, environment), diagnosticsRouteCache, new RouteResolverTrie(new TrieNodeFactory(routeSegmentConstraints)), environment); var diagnosticResponseNegotiator = new DefaultResponseNegotiator(responseProcessors, acceptHeaderCoercionConventions); var diagnosticRouteInvoker = new DefaultRouteInvoker(diagnosticResponseNegotiator); var serializer = new DefaultObjectSerializer(); pipelines.BeforeRequest.AddItemToStartOfPipeline( new PipelineItem>( PipelineKey, ctx => { if (!ctx.ControlPanelEnabled) { return null; } if (!ctx.Request.Path.StartsWith(diagnosticsConfiguration.Path, StringComparison.OrdinalIgnoreCase)) { return null; } if (!diagnosticsConfiguration.Enabled) { return HttpStatusCode.NotFound; } ctx.Items[ItemsKey] = true; var resourcePrefix = string.Concat(diagnosticsConfiguration.Path, "/Resources/"); if (ctx.Request.Path.StartsWith(resourcePrefix, StringComparison.OrdinalIgnoreCase)) { var resourceNamespace = "Nancy.Diagnostics.Resources"; var path = Path.GetDirectoryName(ctx.Request.Url.Path.Replace(resourcePrefix, string.Empty)) ?? string.Empty; if (!string.IsNullOrEmpty(path)) { resourceNamespace += string.Format(".{0}", path.Replace(Path.DirectorySeparatorChar, '.')); } return new EmbeddedFileResponse( typeof(DiagnosticsHook).GetTypeInfo().Assembly, resourceNamespace, Path.GetFileName(ctx.Request.Url.Path)); } RewriteDiagnosticsUrl(diagnosticsConfiguration, ctx); return ValidateConfiguration(diagnosticsConfiguration) ? ExecuteDiagnostics(ctx, diagnosticsRouteResolver, diagnosticsConfiguration, serializer, diagnosticsEnvironment, diagnosticRouteInvoker) : new DiagnosticsViewRenderer(ctx, environment)["help"]; })); } /// /// Gets a special instance that is separate from the /// one used by the application. /// /// private static INancyEnvironment GetDiagnosticsEnvironment() { var diagnosticsEnvironment = new DefaultNancyEnvironment(); diagnosticsEnvironment.Globalization(new[] { "en-US" }); diagnosticsEnvironment.Json(retainCasing: false); diagnosticsEnvironment.AddValue(ViewConfiguration.Default); diagnosticsEnvironment.Tracing( enabled: true, displayErrorTraces: true); return diagnosticsEnvironment; } private static bool ValidateConfiguration(DiagnosticsConfiguration configuration) { return !string.IsNullOrWhiteSpace(configuration.Password) && !string.IsNullOrWhiteSpace(configuration.CookieName) && !string.IsNullOrWhiteSpace(configuration.Path) && configuration.SlidingTimeout != 0; } /// /// Disables the specified pipelines. /// /// /// The pipelines. public static void Disable(IPipelines pipelines) { pipelines.BeforeRequest.RemoveByName(PipelineKey); } private static Response GetDiagnosticsLoginView(NancyContext ctx, INancyEnvironment environment) { var renderer = new DiagnosticsViewRenderer(ctx, environment); return renderer["login"]; } private static Response ExecuteDiagnostics(NancyContext ctx, IRouteResolver routeResolver, DiagnosticsConfiguration diagnosticsConfiguration, DefaultObjectSerializer serializer, INancyEnvironment environment, IRouteInvoker routeInvoker) { var session = GetSession(ctx, diagnosticsConfiguration, serializer); if (session == null) { var view = GetDiagnosticsLoginView(ctx, environment); view.WithCookie( new NancyCookie(diagnosticsConfiguration.CookieName, string.Empty, true) { Expires = DateTime.Now.AddDays(-1) }); return view; } var resolveResult = routeResolver.Resolve(ctx); ctx.Parameters = resolveResult.Parameters; ExecuteRoutePreReq(ctx, CancellationToken, resolveResult.Before); if (ctx.Response == null) { var routeResult = routeInvoker.Invoke(resolveResult.Route, CancellationToken, resolveResult.Parameters, ctx); ctx.Response = routeResult.Result; } if (ctx.Request.Method.Equals("HEAD", StringComparison.OrdinalIgnoreCase)) { ctx.Response = new HeadResponse(ctx.Response); } if (resolveResult.After != null) { resolveResult.After.Invoke(ctx, CancellationToken); } AddUpdateSessionCookie(session, ctx, diagnosticsConfiguration, serializer); return ctx.Response; } private static void AddUpdateSessionCookie(DiagnosticsSession session, NancyContext context, DiagnosticsConfiguration diagnosticsConfiguration, DefaultObjectSerializer serializer) { if (context.Response == null) { return; } session.Expiry = DateTime.Now.AddMinutes(diagnosticsConfiguration.SlidingTimeout); var serializedSession = serializer.Serialize(session); var encryptedSession = diagnosticsConfiguration.CryptographyConfiguration.EncryptionProvider.Encrypt(serializedSession); var hmacBytes = diagnosticsConfiguration.CryptographyConfiguration.HmacProvider.GenerateHmac(encryptedSession); var hmacString = Convert.ToBase64String(hmacBytes); var cookie = new NancyCookie(diagnosticsConfiguration.CookieName, string.Format("{1}{0}", encryptedSession, hmacString), true); context.Response.WithCookie(cookie); } private static DiagnosticsSession GetSession(NancyContext context, DiagnosticsConfiguration diagnosticsConfiguration, DefaultObjectSerializer serializer) { if (context.Request == null) { return null; } if (IsLoginRequest(context, diagnosticsConfiguration)) { return ProcessLogin(context, diagnosticsConfiguration, serializer); } string encryptedValue; if (!context.Request.Cookies.TryGetValue(diagnosticsConfiguration.CookieName, out encryptedValue)) { return null; } var hmacStringLength = Base64Helpers.GetBase64Length(diagnosticsConfiguration.CryptographyConfiguration.HmacProvider.HmacLength); var encryptedSession = encryptedValue.Substring(hmacStringLength); var hmacString = encryptedValue.Substring(0, hmacStringLength); var hmacBytes = Convert.FromBase64String(hmacString); var newHmac = diagnosticsConfiguration.CryptographyConfiguration.HmacProvider.GenerateHmac(encryptedSession); var hmacValid = HmacComparer.Compare(newHmac, hmacBytes, diagnosticsConfiguration.CryptographyConfiguration.HmacProvider.HmacLength); if (!hmacValid) { return null; } var decryptedValue = diagnosticsConfiguration.CryptographyConfiguration.EncryptionProvider.Decrypt(encryptedSession); var session = serializer.Deserialize(decryptedValue) as DiagnosticsSession; if (session == null || session.Expiry < DateTimeOffset.Now || !SessionPasswordValid(session, diagnosticsConfiguration.Password)) { return null; } return session; } private static bool SessionPasswordValid(DiagnosticsSession session, string realPassword) { var newHash = DiagnosticsSession.GenerateSaltedHash(realPassword, session.Salt); return (newHash.Length == session.Hash.Length && newHash.SequenceEqual(session.Hash)); } private static DiagnosticsSession ProcessLogin(NancyContext context, DiagnosticsConfiguration diagnosticsConfiguration, DefaultObjectSerializer serializer) { string password = context.Request.Form.Password; if (!string.Equals(password, diagnosticsConfiguration.Password, StringComparison.Ordinal)) { return null; } var salt = DiagnosticsSession.GenerateRandomSalt(); var hash = DiagnosticsSession.GenerateSaltedHash(password, salt); var session = new DiagnosticsSession { Hash = hash, Salt = salt, Expiry = DateTime.Now.AddMinutes(diagnosticsConfiguration.SlidingTimeout) }; return session; } private static bool IsLoginRequest(NancyContext context, DiagnosticsConfiguration diagnosticsConfiguration) { return context.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase) && context.Request.Url.BasePath.TrimEnd('/').EndsWith(diagnosticsConfiguration.Path) && context.Request.Url.Path == "/"; } private static void ExecuteRoutePreReq(NancyContext context, CancellationToken cancellationToken, BeforePipeline resolveResultPreReq) { if (resolveResultPreReq == null) { return; } var resolveResultPreReqResponse = resolveResultPreReq.Invoke(context, cancellationToken).Result; if (resolveResultPreReqResponse != null) { context.Response = resolveResultPreReqResponse; } } private static void RewriteDiagnosticsUrl(DiagnosticsConfiguration diagnosticsConfiguration, NancyContext ctx) { ctx.Request.Url.BasePath = string.Concat(ctx.Request.Url.BasePath, diagnosticsConfiguration.Path); ctx.Request.Url.Path = ctx.Request.Url.Path.Substring(diagnosticsConfiguration.Path.Length); if (ctx.Request.Url.Path.Length.Equals(0)) { ctx.Request.Url.Path = "/"; } } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsModuleBuilder.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Configuration; using Nancy.ModelBinding; using Nancy.Routing; internal class DiagnosticsModuleBuilder : INancyModuleBuilder { private readonly IRootPathProvider rootPathProvider; private readonly ISerializerFactory serializerFactory; private readonly IModelBinderLocator modelBinderLocator; private readonly INancyEnvironment environment; public DiagnosticsModuleBuilder(IRootPathProvider rootPathProvider, IModelBinderLocator modelBinderLocator, INancyEnvironment diagnosticsEnvironment, INancyEnvironment environment) { this.rootPathProvider = rootPathProvider; this.serializerFactory = new DiagnosticsSerializerFactory(diagnosticsEnvironment); this.modelBinderLocator = modelBinderLocator; this.environment = environment; } /// /// Builds a fully configured instance, based upon the provided . /// /// The that should be configured. /// The current request context. /// A fully configured instance. public INancyModule BuildModule(INancyModule module, NancyContext context) { module.Context = context; module.Response = new DefaultResponseFormatter(rootPathProvider, context, this.serializerFactory, this.environment); module.ModelBinderLocator = this.modelBinderLocator; module.After = new AfterPipeline(); module.Before = new BeforePipeline(); module.OnError = new ErrorPipeline(); return module; } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsModuleCatalog.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; using System.Linq; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Json; using Nancy.ModelBinding; using Nancy.Responses; using Nancy.TinyIoc; internal class DiagnosticsModuleCatalog : INancyModuleCatalog { private readonly TinyIoCContainer container; public DiagnosticsModuleCatalog(IEnumerable providers, IRootPathProvider rootPathProvider, IRequestTracing requestTracing, NancyInternalConfiguration configuration, INancyEnvironment diagnosticsEnvironment, ITypeCatalog typeCatalog, IAssemblyCatalog assemblyCatalog) { this.container = ConfigureContainer(providers, rootPathProvider, requestTracing, configuration, diagnosticsEnvironment, typeCatalog, assemblyCatalog); } /// /// Get all NancyModule implementation instances - should be per-request lifetime /// /// The current context /// An instance containing instances. public IEnumerable GetAllModules(NancyContext context) { return this.container.ResolveAll(false); } /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance public INancyModule GetModule(Type moduleType, NancyContext context) { return this.container.Resolve(moduleType.FullName); } private static TinyIoCContainer ConfigureContainer(IEnumerable providers, IRootPathProvider rootPathProvider, IRequestTracing requestTracing, NancyInternalConfiguration configuration, INancyEnvironment diagnosticsEnvironment, ITypeCatalog typeCatalog, IAssemblyCatalog assemblyCatalog) { var diagContainer = new TinyIoCContainer(); diagContainer.Register(); diagContainer.Register(requestTracing); diagContainer.Register(rootPathProvider); diagContainer.Register(configuration); diagContainer.Register(); diagContainer.Register(); diagContainer.Register(); diagContainer.Register(); diagContainer.Register(diagnosticsEnvironment); diagContainer.Register(new DefaultJsonSerializer(diagnosticsEnvironment)); diagContainer.Register(typeCatalog); diagContainer.Register(assemblyCatalog); foreach (var diagnosticsProvider in providers) { var key = string.Concat( diagnosticsProvider.GetType().FullName, "_", diagnosticsProvider.DiagnosticObject.GetType().FullName); diagContainer.Register(diagnosticsProvider, key); } foreach (var moduleType in typeCatalog.GetTypesAssignableTo()) { diagContainer.Register(typeof(INancyModule), moduleType, moduleType.FullName).AsMultiInstance(); } return diagContainer; } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsSerializerFactory.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Configuration; using Nancy.Responses; using Nancy.Responses.Negotiation; internal class DiagnosticsSerializerFactory : ISerializerFactory { private readonly ISerializer serializer; public DiagnosticsSerializerFactory(INancyEnvironment diagnosticsEnvironment) { this.serializer = new DefaultJsonSerializer(diagnosticsEnvironment); } /// /// Gets the implementation that can serialize the provided . /// /// The to get a serializer for. /// An instance, or if not match was found. public ISerializer GetSerializer(MediaRange mediaRange) { return this.serializer; } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsSession.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Security.Cryptography; using System.Text; /// /// Stores the http session information for diagnostics. /// #if !NETSTANDARD1_6 [Serializable] #endif public class DiagnosticsSession { /// /// Gets or sets the hash. /// /// The (salted) SHA256 hash. public byte[] Hash { get; set; } /// /// Gets or sets the salt. /// /// The salt for the hash value. public byte[] Salt { get; set; } /// /// Gets or sets the expiry. /// /// The time when the session will be expired. public DateTimeOffset Expiry { get; set; } /// /// Generates a random salt. /// /// A byte array representing the random salt. public static byte[] GenerateRandomSalt() { var provider = RandomNumberGenerator.Create(); var buffer = new byte[32]; provider.GetBytes(buffer); return buffer; } /// /// Generates the salted hash of a byte array. /// /// The plain text as array. /// The salt as array. /// A byte array representing the salted hash. public static byte[] GenerateSaltedHash(byte[] plainText, byte[] salt) { var algorithm = SHA256.Create(); var plainTextWithSaltBytes = new byte[plainText.Length + salt.Length]; for (var i = 0; i < plainText.Length; i++) { plainTextWithSaltBytes[i] = plainText[i]; } for (var i = 0; i < salt.Length; i++) { plainTextWithSaltBytes[plainText.Length + i] = salt[i]; } return algorithm.ComputeHash(plainTextWithSaltBytes); } /// /// Generates the salted hash of a . /// /// The plain text as /// The salt as array. /// A byte array representing the salted hash. public static byte[] GenerateSaltedHash(string plainText, byte[] salt) { return GenerateSaltedHash(Encoding.UTF8.GetBytes(plainText), salt); } } } ================================================ FILE: src/Nancy/Diagnostics/DiagnosticsViewRenderer.cs ================================================ namespace Nancy.Diagnostics { using System.IO; using System.Linq; using System.Reflection; using Configuration; using Nancy.Localization; using Nancy.Responses; using Nancy.Security; using Nancy.ViewEngines; using Nancy.ViewEngines.SuperSimpleViewEngine; /// /// Renders diagnostics views from embedded resources. /// public class DiagnosticsViewRenderer { private readonly NancyContext context; private readonly INancyEnvironment environment; private static readonly IViewResolver ViewResolver = new DiagnosticsViewResolver(); private static readonly IViewEngine Engine = new SuperSimpleViewEngineWrapper(Enumerable.Empty()); /// /// Creates a new instance of the class. /// /// A instance. /// An instance. public DiagnosticsViewRenderer(NancyContext context, INancyEnvironment environment) { this.context = context; this.environment = environment; } /// /// Renders the diagnostics view with the provided . /// /// The name of the view to render. /// A of the rendered view. public Response this[string name] { get { return this.RenderView(name, null, this.context); } } /// /// Renders the diagnostics view with the provided and . /// /// The name of the view to render. /// The model that should be passed to the view engine during rendering. /// A of the rendered view. public Response this[string name, dynamic model] { get { return RenderView(name, model, this.context); } } private Response RenderView(string name, dynamic model, NancyContext context) { var fullName = string.Concat(name, ".sshtml"); var stream = GetBodyStream(fullName); var location = GetViewLocationResult(fullName, stream); var cache = new DefaultViewCache(this.environment); context.Items.Add(CsrfToken.DEFAULT_CSRF_KEY, "DIAGNOSTICSTOKEN"); var renderContext = new DefaultRenderContext(ViewResolver, cache, new DummyTextResource(), new ViewLocationContext() { Context = context }); return Engine.RenderView(location, model, renderContext); } private static Stream GetBodyStream(string name) { var view = new EmbeddedFileResponse(typeof(DiagnosticsViewRenderer).GetTypeInfo().Assembly, "Nancy.Diagnostics.Views", name); var stream = new MemoryStream(); view.Contents.Invoke(stream); stream.Position = 0; return stream; } private static ViewLocationResult GetViewLocationResult(string name, Stream bodyStream) { return new ViewLocationResult( "Nancy/Diagnostics/Views", name, "sshtml", () => new StreamReader(bodyStream)); } internal class DiagnosticsViewResolver : IViewResolver { /// /// Locates a view based on the provided information. /// /// The name of the view to locate. /// The model that will be used with the view. /// A instance, containing information about the context for which the view is being located. /// A instance if the view could be found, otherwise . public ViewLocationResult GetViewLocation(string viewName, dynamic model, ViewLocationContext viewLocationContext) { var fullName = string.Concat(viewName, ".sshtml"); var stream = GetBodyStream(fullName); return GetViewLocationResult(fullName, stream); } } internal class DummyTextResource : ITextResource { public string this[string key, NancyContext context] { get { return string.Empty; } } } } } ================================================ FILE: src/Nancy/Diagnostics/DisabledDiagnostics.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Bootstrapper; /// /// Implementation of the interface that does nothing. /// public class DisabledDiagnostics : IDiagnostics { /// /// Initialise diagnostics /// /// Application pipelines public void Initialize(IPipelines pipelines) { // Do nothing :-) } } } ================================================ FILE: src/Nancy/Diagnostics/IDiagnostics.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Bootstrapper; /// /// Defines the functionality for Nancy diagnostics. /// public interface IDiagnostics { /// /// Initializes diagnostics /// /// Application pipelines void Initialize(IPipelines pipelines); } } ================================================ FILE: src/Nancy/Diagnostics/IDiagnosticsProvider.cs ================================================ namespace Nancy.Diagnostics { /// /// Defines the functionality a diagnostics provider. /// public interface IDiagnosticsProvider { /// /// Gets the name of the provider. /// /// A containing the name of the provider. string Name { get; } /// /// Gets the description of the provider. /// /// A containing the description of the provider. string Description { get; } /// /// Gets the object that contains the interactive diagnostics methods. /// /// An instance of the interactive diagnostics object. object DiagnosticObject { get; } } } ================================================ FILE: src/Nancy/Diagnostics/IInteractiveDiagnostics.cs ================================================ namespace Nancy.Diagnostics { using System.Collections.Generic; /// /// Defines the functionality for Nancy interactive diagnostics /// public interface IInteractiveDiagnostics { /// /// Gets the list of available . /// /// The available diagnostics. IEnumerable AvailableDiagnostics { get; } /// /// Executes the . /// /// The instance /// The arguments. /// The result of the as object ExecuteDiagnostic(InteractiveDiagnosticMethod interactiveDiagnosticMethod, object[] arguments); /// /// Gets the template. /// /// The instance /// The template as string GetTemplate(InteractiveDiagnosticMethod interactiveDiagnosticMethod); /// /// Gets the . /// /// Name of the provider. /// The instance. InteractiveDiagnostic GetDiagnostic(string providerName); /// /// Gets the . /// /// Name of the provider. /// Name of the method. /// The instance InteractiveDiagnosticMethod GetMethod(string providerName, string methodName); } } ================================================ FILE: src/Nancy/Diagnostics/IRequestTrace.cs ================================================ namespace Nancy.Diagnostics { using System.Collections.Generic; /// /// Defines the functionality for tracing a request. /// public interface IRequestTrace { /// /// Gets or sets the generic item store. /// /// An instance containing the items. IDictionary Items { get; set; } /// /// Gets or sets the information about the request. /// /// An instance. RequestData RequestData { get; set; } /// /// Gets or sets the information about the response. /// /// An instance. ResponseData ResponseData { get; set; } /// /// Gets the trace log. /// /// A instance. ITraceLog TraceLog { get; set; } } } ================================================ FILE: src/Nancy/Diagnostics/IRequestTraceFactory.cs ================================================ namespace Nancy.Diagnostics { /// /// Defines the functionality for creating an instance. /// public interface IRequestTraceFactory { /// /// Creates an instance. /// /// A instance. /// An instance. IRequestTrace Create(Request request); } } ================================================ FILE: src/Nancy/Diagnostics/IRequestTracing.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; /// /// Defines the functionality for request tracing. /// public interface IRequestTracing { /// /// Adds the , of the provided, to the trace log. /// /// The identifier of the trace. /// A instance. void AddRequestDiagnosticToSession(Guid sessionId, NancyContext context); /// /// Clears the trace log. /// void Clear(); /// /// Creates a new trace session. /// /// A which represents the identifier of the new trace session. Guid CreateSession(); /// /// Gets all the available instances. /// /// IEnumerable GetSessions(); /// /// Checks if the provided is valid or not. /// /// A representing the session to check. /// if the session is valid, otherwise . bool IsValidSessionId(Guid sessionId); } } ================================================ FILE: src/Nancy/Diagnostics/ITraceLog.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Text; /// /// Provides request trace logging. /// Uses a delegate to write to the log, rather than creating strings regardless /// of whether the log is enabled or not. /// public interface ITraceLog { /// /// Write to the log /// /// Log writing delegate void WriteLog(Action logDelegate); } } ================================================ FILE: src/Nancy/Diagnostics/InteractiveDiagnostic.cs ================================================ namespace Nancy.Diagnostics { using System.Collections.Generic; /// /// An interactive diagnostic instance. /// public class InteractiveDiagnostic { /// /// Gets or sets the diagnostic name. /// /// The name of the diagnostic public string Name { get; set; } /// /// Gets or sets the diagnostic description. /// /// The description of the diagnostic. public string Description { get; set; } /// /// Gets or sets the diagnostic methods. /// /// The collection of diagnostic methods. public IEnumerable Methods { get; set; } } } ================================================ FILE: src/Nancy/Diagnostics/InteractiveDiagnosticMethod.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; /// /// Defines an interactive diagnostic method. /// public class InteractiveDiagnosticMethod { /// /// Gets the parent diagnostic object. /// /// The parent diagnostic object. public object ParentDiagnosticObject { get; private set; } /// /// Gets the return type /// /// The type of the method return type public Type ReturnType { get; private set; } /// /// Gets the name of the method. /// /// The name of the method. public string MethodName { get; private set; } /// /// Gets the description. /// /// The description of the method. public string Description { get; private set; } /// /// Gets the arguments. /// /// The arguments for the method. public IEnumerable> Arguments { get; private set; } /// /// Initializes a new instance of the class, with /// the provided , , /// , and . /// /// The parent diagnostic. /// Type of the return. /// Name of the method. /// The arguments. /// The description. public InteractiveDiagnosticMethod(object parentDiagnostic, Type returnType, string methodName, IEnumerable> arguments, string description) { this.ParentDiagnosticObject = parentDiagnostic; this.ReturnType = returnType; this.MethodName = methodName; this.Arguments = arguments; this.Description = description; } } } ================================================ FILE: src/Nancy/Diagnostics/InteractiveDiagnostics.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Routing; /// /// Handles interactive diagnostic instances. /// /// public class InteractiveDiagnostics : IInteractiveDiagnostics { private readonly IDiagnosticsProvider[] providers; private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; /// /// Gets the list of available diagnostics. /// /// The available diagnostics. public IEnumerable AvailableDiagnostics { get; private set; } /// /// Initializes an class of instances. /// /// The providers. public InteractiveDiagnostics(IEnumerable providers) { var customProvidersAvailable = providers.Any(provider => { Type providerType = provider.GetType(); return providerType != typeof(TestingDiagnosticProvider) & providerType != typeof(DefaultRouteCacheProvider); }); if (customProvidersAvailable) { // Exclude only the TestingDiagnosticProvider this.providers = providers.Where(provider => provider.GetType() != typeof(TestingDiagnosticProvider)).ToArray(); } else { this.providers = providers.ToArray(); } this.BuildAvailableDiagnostics(); } /// /// Executes the diagnostic. /// /// The interactive diagnostic method. /// The arguments. /// The result of the as /// public object ExecuteDiagnostic(InteractiveDiagnosticMethod interactiveDiagnosticMethod, object[] arguments) { var method = GetMethodInfo(interactiveDiagnosticMethod); if (method == null) { throw new ArgumentException(string.Format("Unable to locate method: {0}", interactiveDiagnosticMethod.MethodName)); } return method.Invoke(interactiveDiagnosticMethod.ParentDiagnosticObject, arguments); } /// /// Gets the template for an interactive diagnostic method instance. /// /// The interactive diagnostic method. /// The template as public string GetTemplate(InteractiveDiagnosticMethod interactiveDiagnosticMethod) { var diagObjectType = interactiveDiagnosticMethod.ParentDiagnosticObject.GetType(); return GetTemplateFromProperty(interactiveDiagnosticMethod, diagObjectType) ?? GetTemplateFromAttribute(interactiveDiagnosticMethod); } /// /// Gets the diagnostic for a provider. /// /// Name of the provider. /// The instance. public InteractiveDiagnostic GetDiagnostic(string providerName) { return this.AvailableDiagnostics.FirstOrDefault(d => string.Equals(d.Name, providerName, StringComparison.OrdinalIgnoreCase)); } /// /// Gets the method instance for a method name and provider. /// /// Name of the provider. /// Name of the method. /// The instance public InteractiveDiagnosticMethod GetMethod(string providerName, string methodName) { var diagnostic = this.GetDiagnostic(providerName); if (diagnostic == null) { return null; } return diagnostic.Methods.FirstOrDefault(m => string.Equals(m.MethodName, methodName, StringComparison.OrdinalIgnoreCase)); } private void BuildAvailableDiagnostics() { var diags = new List(this.providers.Length); foreach (var diagnosticsProvider in this.providers) { diags.Add(new InteractiveDiagnostic { Name = diagnosticsProvider.Name, Description = diagnosticsProvider.Description, Methods = this.GetDiagnosticMethods(diagnosticsProvider) }); } this.AvailableDiagnostics = diags; } private IEnumerable GetDiagnosticMethods(IDiagnosticsProvider diagnosticsProvider) { var objectMethods = typeof(object).GetMethods().Select(x => x.Name).ToList(); var methods = diagnosticsProvider.DiagnosticObject .GetType() .GetMethods(Flags) .Where(x => !objectMethods.Contains(x.Name)) .Where(mi => !mi.IsSpecialName) .ToArray(); var diagnosticMethods = new List(methods.Length); foreach (var methodInfo in methods) { diagnosticMethods.Add(new InteractiveDiagnosticMethod( diagnosticsProvider.DiagnosticObject, methodInfo.ReturnType, methodInfo.Name, this.GetArguments(methodInfo), this.GetDescription(diagnosticsProvider, methodInfo))); } return diagnosticMethods; } private string GetDescription(IDiagnosticsProvider diagnosticsProvider, MethodInfo methodInfo) { return GetDescriptionFromProperty(diagnosticsProvider, methodInfo) ?? GetDescriptionFromAttribute(diagnosticsProvider, methodInfo); } private IEnumerable> GetArguments(MethodInfo methodInfo) { var parameters = methodInfo.GetParameters(); var arguments = new List>(parameters.Length); foreach (var parameterInfo in parameters) { arguments.Add(Tuple.Create(parameterInfo.Name, parameterInfo.ParameterType)); } return arguments; } private static string GetTemplateFromProperty( InteractiveDiagnosticMethod interactiveDiagnosticMethod, Type diagObjectType) { var propertyName = String.Format("{0}{1}", interactiveDiagnosticMethod.MethodName, "Template"); var property = diagObjectType.GetProperty(propertyName); if (property == null) { return null; } return (string)property.GetValue(interactiveDiagnosticMethod.ParentDiagnosticObject, null); } private static string GetTemplateFromAttribute(InteractiveDiagnosticMethod interactiveDiagnosticMethod) { var method = GetMethodInfo(interactiveDiagnosticMethod); var attribute = (TemplateAttribute)method.GetCustomAttribute(typeof(TemplateAttribute)); return attribute != null ? attribute.Template : null; } private static string GetDescriptionFromProperty(IDiagnosticsProvider diagnosticsProvider, MethodInfo methodInfo) { var propertyName = String.Format("{0}{1}", methodInfo.Name, "Description"); var property = diagnosticsProvider.DiagnosticObject.GetType().GetProperty(propertyName); if (property == null) { return null; } return (string)property.GetValue(diagnosticsProvider.DiagnosticObject, null); } private static string GetDescriptionFromAttribute(IDiagnosticsProvider diagnosticsProvider, MethodInfo methodInfo) { var attribute = (DescriptionAttribute)methodInfo.GetCustomAttribute(typeof(DescriptionAttribute)); return attribute != null ? attribute.Description : null; } private static MethodInfo GetMethodInfo(InteractiveDiagnosticMethod interactiveDiagnosticMethod) { var diagObjectType = interactiveDiagnosticMethod.ParentDiagnosticObject.GetType(); var method = diagObjectType.GetMethod(interactiveDiagnosticMethod.MethodName, Flags); return method; } } } ================================================ FILE: src/Nancy/Diagnostics/Modules/InfoModule.cs ================================================ namespace Nancy.Diagnostics.Modules { using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Reflection; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.ViewEngines; /// /// The information module for diagnostics. /// /// public class InfoModule : DiagnosticModule { private readonly ITypeCatalog typeCatalog; private readonly IAssemblyCatalog assemblyCatalog; /// /// Initializes a new instance of the class, with /// the provided , , /// , and . /// /// The root path provider. /// The configuration. /// The environment. /// The type catalog. /// The assembly catalog. public InfoModule(IRootPathProvider rootPathProvider, NancyInternalConfiguration configuration, INancyEnvironment environment, ITypeCatalog typeCatalog, IAssemblyCatalog assemblyCatalog) : base("/info") { this.typeCatalog = typeCatalog; this.assemblyCatalog = assemblyCatalog; Get("/", _ => { return View["Info"]; }); Get("/data", _ => { dynamic data = new ExpandoObject(); data.Nancy = new ExpandoObject(); data.Nancy.Version = string.Format("v{0}", this.GetType().GetTypeInfo().Assembly.GetName().Version.ToString()); data.Nancy.TracesDisabled = !environment.GetValue().DisplayErrorTraces; data.Nancy.CaseSensitivity = StaticConfiguration.CaseSensitive ? "Sensitive" : "Insensitive"; data.Nancy.RootPath = rootPathProvider.GetRootPath(); data.Nancy.Hosting = GetHosting(); data.Nancy.BootstrapperContainer = GetBootstrapperContainer(); data.Nancy.LocatedBootstrapper = NancyBootstrapperLocator.GetBootstrapperType().ToString(); data.Nancy.LoadedViewEngines = GetViewEngines(); data.Configuration = new Dictionary(); foreach (var propertyInfo in configuration.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) { var value = propertyInfo.GetValue(configuration, null); data.Configuration[propertyInfo.Name] = (!typeof(IEnumerable).IsAssignableFrom(value.GetType())) ? new[] { value.ToString() } : ((IEnumerable)value).Select(x => x.ToString()); } return this.Response.AsJson((object)data); }); } private string[] GetViewEngines() { var engines = this.typeCatalog.GetTypesAssignableTo(); return engines .Select(engine => engine.Name.Split(new [] { "ViewEngine" }, StringSplitOptions.None)[0]) .ToArray(); } private string GetBootstrapperContainer() { var name = this.assemblyCatalog .GetAssemblies() .Select(asm => asm.GetName()) .FirstOrDefault(asmName => asmName.Name != null && asmName.Name.StartsWith("Nancy.Bootstrappers.")); return (name == null) ? "TinyIoC" : string.Format("{0} (v{1})", name.Name.Split('.').Last(), name.Version); } private string GetHosting() { var name = this.assemblyCatalog .GetAssemblies() .Select(asm => asm.GetName()) .FirstOrDefault(asmName => asmName.Name != null && asmName.Name.StartsWith("Nancy.Hosting.")); return (name == null) ? "Unknown" : string.Format("{0} (v{1})", name.Name.Split('.').Last(), name.Version); } } } ================================================ FILE: src/Nancy/Diagnostics/Modules/InteractiveModule.cs ================================================ namespace Nancy.Diagnostics.Modules { using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using Nancy.Helpers; /// /// Nancy module for interactive diagnostics. /// /// public class InteractiveModule : DiagnosticModule { private readonly IInteractiveDiagnostics interactiveDiagnostics; /// /// Initializes a new instance of the class, with /// the provided . /// /// The interactive diagnostics. public InteractiveModule(IInteractiveDiagnostics interactiveDiagnostics) :base ("/interactive") { this.interactiveDiagnostics = interactiveDiagnostics; Get("/", _ => { return View["InteractiveDiagnostics"]; }); Get("/providers", _ => { var providers = this.interactiveDiagnostics .AvailableDiagnostics .Select(p => new { p.Name, p.Description, Type = p.GetType().Name, p.GetType().Namespace, Assembly = p.GetType().GetTypeInfo().Assembly.GetName().Name }) .ToArray(); return this.Response.AsJson(providers); }); Get("/providers/{providerName}", ctx => { var providerName = HttpUtility.UrlDecode((string)ctx.providerName); var diagnostic = this.interactiveDiagnostics.GetDiagnostic(providerName); if (diagnostic == null) { return HttpStatusCode.NotFound; } var methods = diagnostic.Methods .Select(m => new { m.MethodName, ReturnType = m.ReturnType.ToString(), m.Description, Arguments = m.Arguments.Select(a => new { ArgumentName = a.Item1, ArgumentType = a.Item2.ToString() }) }) .ToArray(); return this.Response.AsJson(methods); }); Get("/providers/{providerName}/{methodName}", ctx => { var providerName = HttpUtility.UrlDecode((string)ctx.providerName); var methodName = HttpUtility.UrlDecode((string)ctx.methodName); var method = this.interactiveDiagnostics.GetMethod(providerName, methodName); if (method == null) { return HttpStatusCode.NotFound; } object[] arguments = GetArguments(method, this.Request.Query); return this.Response.AsJson(new { Result = this.interactiveDiagnostics.ExecuteDiagnostic(method, arguments) }); }); Get("/templates/{providerName}/{methodName}", ctx => { var providerName = HttpUtility.UrlDecode((string)ctx.providerName); var methodName = HttpUtility.UrlDecode((string)ctx.methodName); var method = this.interactiveDiagnostics.GetMethod(providerName, methodName); if (method == null) { return HttpStatusCode.NotFound; } var template = this.interactiveDiagnostics.GetTemplate(method); if (template == null) { return HttpStatusCode.NotFound; } return template; }); } private static object[] GetArguments(InteractiveDiagnosticMethod method, dynamic query) { var arguments = new List(); foreach (var argument in method.Arguments) { arguments.Add(ConvertArgument((string)query[argument.Item1].Value, argument.Item2)); } return arguments.ToArray(); } private static object ConvertArgument(string value, Type destinationType) { var converter = TypeDescriptor.GetConverter(destinationType); if (converter == null || !converter.CanConvertFrom(typeof(string))) { return null; } try { return converter.ConvertFrom(value); } catch (FormatException) { return null; } } } } ================================================ FILE: src/Nancy/Diagnostics/Modules/MainModule.cs ================================================ namespace Nancy.Diagnostics.Modules { /// /// Main Nancy module for diagnostics. /// /// public class MainModule : DiagnosticModule { /// /// Initializes a new instance of the class. /// public MainModule() { Get("/", _ => { return View["Dashboard"]; }); Post("/", _ => { return this.Response.AsRedirect("~/"); }); } } } ================================================ FILE: src/Nancy/Diagnostics/Modules/SettingsModule.cs ================================================ namespace Nancy.Diagnostics.Modules { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Nancy.ModelBinding; /// /// Nancy module for diagnostic settings. /// /// public class SettingsModule : DiagnosticModule { private static readonly IEnumerable Types = new[] { typeof(StaticConfiguration) }.Union( typeof(StaticConfiguration).GetNestedTypes(BindingFlags.Static | BindingFlags.Public)); /// /// Initializes a new instance of the class. /// public SettingsModule() : base("/settings") { Get("/", _ => { var properties = Types.SelectMany(t => t.GetProperties(BindingFlags.Static | BindingFlags.Public)) .Where(x => x.PropertyType == typeof(bool)); var model = from property in properties orderby property.Name let value = (bool) property.GetValue(null, null) let description = GetDescription(property) where !string.IsNullOrEmpty(description) select new { Name = property.Name, Description = description, DisplayName = Regex.Replace(property.Name, "[A-Z]", " $0"), Value = value, Checked = (value) ? "checked='checked'" : string.Empty }; return View["Settings", model]; }); Post("/", _ => { var model = this.Bind(); var property = GetProperty(model); if (property != null) { property.SetValue(null, model.Value, null); } return HttpStatusCode.OK; }); } private static PropertyInfo GetProperty(SettingsModel model) { return Types.SelectMany(t => t.GetProperties(BindingFlags.Static | BindingFlags.Public)) .SingleOrDefault(x => x.Name.Equals(model.Name, StringComparison.OrdinalIgnoreCase)); } private static string GetDescription(PropertyInfo property) { var attributes = property .GetCustomAttributes(typeof (DescriptionAttribute), false) .Cast() .ToArray(); return (!attributes.Any()) ? string.Empty : attributes.First().Description; } } /// /// Data model for settings. /// public class SettingsModel { /// /// Gets or sets the name for the setting. /// /// The name of the setting public string Name { get; set; } /// /// Gets or sets the value for this setting. /// /// or public bool Value { get; set; } } } ================================================ FILE: src/Nancy/Diagnostics/Modules/TraceModule.cs ================================================ namespace Nancy.Diagnostics.Modules { using System; using System.Linq; /// /// Nancy module for request tracing. Part of diagnostics module. /// /// public class TraceModule : DiagnosticModule { private readonly IRequestTracing sessionProvider; /// /// Initializes an instance of the class, with /// the provided . /// /// The session provider. public TraceModule(IRequestTracing sessionProvider) : base("/trace") { this.sessionProvider = sessionProvider; Get("/", _ => { return View["RequestTracing"]; }); Get("/sessions", _ => { return this.Response.AsJson(this.sessionProvider.GetSessions().Select(s => new { Id = s.Id }).ToArray()); }); Get("/sessions/{id}", ctx => { Guid id; if (!Guid.TryParse(ctx.Id, out id)) { return HttpStatusCode.NotFound; } var session = this.sessionProvider.GetSessions().FirstOrDefault(s => s.Id == id); if (session == null) { return HttpStatusCode.NotFound; } return this.Response.AsJson(session.RequestTraces.Select(t => new { t.RequestData.Method, RequestUrl = t.RequestData.Url, RequestContentType = t.RequestData.ContentType, ResponseContentType = t.ResponseData.ContentType, RequestHeaders = t.RequestData.Headers, ResponseHeaders = t.ResponseData.Headers, t.ResponseData.StatusCode, Log = t.TraceLog.ToString().Replace("\r", "").Split(new[] { "\n" }, StringSplitOptions.None), }).ToArray()); }); } } } ================================================ FILE: src/Nancy/Diagnostics/NullLog.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Text; /// /// Implementation of that does not log anything. /// public class NullLog : ITraceLog { /// /// Write to the log /// /// Log writing delegate public void WriteLog(Action logDelegate) { } /// /// Returns a string that represents the current object. /// /// /// A string that represents the current object. /// public override string ToString() { return string.Empty; } } } ================================================ FILE: src/Nancy/Diagnostics/RequestData.cs ================================================ namespace Nancy.Diagnostics { using Nancy.Responses.Negotiation; /// /// Stores request trace information about the request. /// public class RequestData { /// /// Gets or sets the content type of the request. /// /// A containing the content type. public MediaRange ContentType { get; set; } /// /// Gets or sets the headers of the request. /// /// A instance containing the headers. public RequestHeaders Headers { get; set; } /// /// Gets the HTTP verb of the request. /// /// A containing the HTTP verb. public string Method { get; set; } /// /// Gets or sets the that was requested. /// public Url Url { get; set; } /// /// Implicitly casts a instance into a instance. /// /// A instance. /// A instance. public static implicit operator RequestData(Request request) { return new RequestData { ContentType = request.Headers.ContentType, Headers = request.Headers, Method = request.Method, Url = request.Url }; } } } ================================================ FILE: src/Nancy/Diagnostics/RequestTraceSession.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; /// /// Holds trace sessions for a request. /// public class RequestTraceSession { private const int MaxSize = 500; private readonly ConcurrentLimitedCollection requestTraces; /// /// Initializes an instance of the class, with /// the provided . /// /// The session identifier. public RequestTraceSession(Guid id) { this.Id = id; this.requestTraces = new ConcurrentLimitedCollection(MaxSize); } /// /// Gets the identifier. /// /// The session identifier. public Guid Id { get; private set; } /// /// Gets the request traces. /// /// The collection of request traces. public IEnumerable RequestTraces { get { return this.requestTraces; } } /// /// Adds a request trace instance to the collection. /// /// The trace. public void AddRequestTrace(IRequestTrace trace) { this.requestTraces.Add(trace); } } } ================================================ FILE: src/Nancy/Diagnostics/Resources/960.css ================================================ body{min-width:960px}.container_12,.container_16{margin-left:auto;margin-right:auto;width:960px}.grid_1,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9,.grid_10,.grid_11,.grid_12,.grid_13,.grid_14,.grid_15,.grid_16{display:inline;float:left;margin-left:10px;margin-right:10px}.push_1,.pull_1,.push_2,.pull_2,.push_3,.pull_3,.push_4,.pull_4,.push_5,.pull_5,.push_6,.pull_6,.push_7,.pull_7,.push_8,.pull_8,.push_9,.pull_9,.push_10,.pull_10,.push_11,.pull_11,.push_12,.pull_12,.push_13,.pull_13,.push_14,.pull_14,.push_15,.pull_15{position:relative}.container_12 .grid_3,.container_16 .grid_4{width:220px}.container_12 .grid_6,.container_16 .grid_8{width:460px}.container_12 .grid_9,.container_16 .grid_12{width:700px}.container_12 .grid_12,.container_16 .grid_16{width:940px}.alpha{margin-left:0}.omega{margin-right:0}.container_12 .grid_1{width:60px}.container_12 .grid_2{width:140px}.container_12 .grid_4{width:300px}.container_12 .grid_5{width:380px}.container_12 .grid_7{width:540px}.container_12 .grid_8{width:620px}.container_12 .grid_10{width:780px}.container_12 .grid_11{width:860px}.container_16 .grid_1{width:40px}.container_16 .grid_2{width:100px}.container_16 .grid_3{width:160px}.container_16 .grid_5{width:280px}.container_16 .grid_6{width:340px}.container_16 .grid_7{width:400px}.container_16 .grid_9{width:520px}.container_16 .grid_10{width:580px}.container_16 .grid_11{width:640px}.container_16 .grid_13{width:760px}.container_16 .grid_14{width:820px}.container_16 .grid_15{width:880px}.container_12 .prefix_3,.container_16 .prefix_4{padding-left:240px}.container_12 .prefix_6,.container_16 .prefix_8{padding-left:480px}.container_12 .prefix_9,.container_16 .prefix_12{padding-left:720px}.container_12 .prefix_1{padding-left:80px}.container_12 .prefix_2{padding-left:160px}.container_12 .prefix_4{padding-left:320px}.container_12 .prefix_5{padding-left:400px}.container_12 .prefix_7{padding-left:560px}.container_12 .prefix_8{padding-left:640px}.container_12 .prefix_10{padding-left:800px}.container_12 .prefix_11{padding-left:880px}.container_16 .prefix_1{padding-left:60px}.container_16 .prefix_2{padding-left:120px}.container_16 .prefix_3{padding-left:180px}.container_16 .prefix_5{padding-left:300px}.container_16 .prefix_6{padding-left:360px}.container_16 .prefix_7{padding-left:420px}.container_16 .prefix_9{padding-left:540px}.container_16 .prefix_10{padding-left:600px}.container_16 .prefix_11{padding-left:660px}.container_16 .prefix_13{padding-left:780px}.container_16 .prefix_14{padding-left:840px}.container_16 .prefix_15{padding-left:900px}.container_12 .suffix_3,.container_16 .suffix_4{padding-right:240px}.container_12 .suffix_6,.container_16 .suffix_8{padding-right:480px}.container_12 .suffix_9,.container_16 .suffix_12{padding-right:720px}.container_12 .suffix_1{padding-right:80px}.container_12 .suffix_2{padding-right:160px}.container_12 .suffix_4{padding-right:320px}.container_12 .suffix_5{padding-right:400px}.container_12 .suffix_7{padding-right:560px}.container_12 .suffix_8{padding-right:640px}.container_12 .suffix_10{padding-right:800px}.container_12 .suffix_11{padding-right:880px}.container_16 .suffix_1{padding-right:60px}.container_16 .suffix_2{padding-right:120px}.container_16 .suffix_3{padding-right:180px}.container_16 .suffix_5{padding-right:300px}.container_16 .suffix_6{padding-right:360px}.container_16 .suffix_7{padding-right:420px}.container_16 .suffix_9{padding-right:540px}.container_16 .suffix_10{padding-right:600px}.container_16 .suffix_11{padding-right:660px}.container_16 .suffix_13{padding-right:780px}.container_16 .suffix_14{padding-right:840px}.container_16 .suffix_15{padding-right:900px}.container_12 .push_3,.container_16 .push_4{left:240px}.container_12 .push_6,.container_16 .push_8{left:480px}.container_12 .push_9,.container_16 .push_12{left:720px}.container_12 .push_1{left:80px}.container_12 .push_2{left:160px}.container_12 .push_4{left:320px}.container_12 .push_5{left:400px}.container_12 .push_7{left:560px}.container_12 .push_8{left:640px}.container_12 .push_10{left:800px}.container_12 .push_11{left:880px}.container_16 .push_1{left:60px}.container_16 .push_2{left:120px}.container_16 .push_3{left:180px}.container_16 .push_5{left:300px}.container_16 .push_6{left:360px}.container_16 .push_7{left:420px}.container_16 .push_9{left:540px}.container_16 .push_10{left:600px}.container_16 .push_11{left:660px}.container_16 .push_13{left:780px}.container_16 .push_14{left:840px}.container_16 .push_15{left:900px}.container_12 .pull_3,.container_16 .pull_4{left:-240px}.container_12 .pull_6,.container_16 .pull_8{left:-480px}.container_12 .pull_9,.container_16 .pull_12{left:-720px}.container_12 .pull_1{left:-80px}.container_12 .pull_2{left:-160px}.container_12 .pull_4{left:-320px}.container_12 .pull_5{left:-400px}.container_12 .pull_7{left:-560px}.container_12 .pull_8{left:-640px}.container_12 .pull_10{left:-800px}.container_12 .pull_11{left:-880px}.container_16 .pull_1{left:-60px}.container_16 .pull_2{left:-120px}.container_16 .pull_3{left:-180px}.container_16 .pull_5{left:-300px}.container_16 .pull_6{left:-360px}.container_16 .pull_7{left:-420px}.container_16 .pull_9{left:-540px}.container_16 .pull_10{left:-600px}.container_16 .pull_11{left:-660px}.container_16 .pull_13{left:-780px}.container_16 .pull_14{left:-840px}.container_16 .pull_15{left:-900px}.clear{clear:both;display:block;overflow:hidden;visibility:hidden;width:0;height:0}.clearfix:before,.clearfix:after,.container_12:before,.container_12:after,.container_16:before,.container_16:after{content:'.';display:block;overflow:hidden;visibility:hidden;font-size:0;line-height:0;width:0;height:0}.clearfix:after,.container_12:after,.container_16:after{clear:both}.clearfix,.container_12,.container_16{zoom:1} ================================================ FILE: src/Nancy/Diagnostics/Resources/Modules/interactive/methods.js ================================================ (function (Method) { var app = diagnostics.app; Method.Model = Backbone.Model.extend({}); Method.Collection = Backbone.Collection.extend({ model: Method.Model, initialize: function (opts) { this.providerName = opts.providerName; }, url: function () { return Nancy.config.basePath + "interactive/providers/" + this.providerName; } }); Method.Views.List = Backbone.View.extend({ el: '#main', events: { 'click #back': 'back' }, initialize: function () { this.router = app.router; this.template = $("#methodList").html(); this.providerName = this.model.providerName; }, render: function () { var methods = this.model.toJSON(); var html = Handlebars.compile(this.template)({ collection: methods }); $(this.el).html(html); _.each(methods, this.renderItem, this); }, renderItem: function (model) { var itemView = new Method.Views.Item({ model: model, providerName: this.providerName }); this.$('#root').append(itemView.el); }, back: function () { this.router.navigate("", true); } }); Method.Views.Item = Backbone.View.extend({ tagName: 'li', events: { 'focus .method-argument input': 'showTooltip', 'blur .method-argument input': 'hideTooltip', 'click input[type=button]': 'executeMethod' }, initialize: function (args) { this.providerName = args.providerName; this.app = app; this.router = app.router; this.template = $("#method").html(); this.render(); }, render: function () { var html = Handlebars.compile(this.template)({ model: this.model }); $(this.el).append(html); }, showTooltip: function (e) { $(e.currentTarget).parent('.tooltip').addClass("tooltip-showing"); }, hideTooltip: function (e) { $(e.currentTarget).parent('.tooltip').removeClass("tooltip-showing"); }, executeMethod: function () { var parameters = this.$("input"); var executionContext = {}; executionContext.providerName = this.providerName; executionContext.methodName = this.model.methodName; executionContext.arguments = []; _.each(parameters, function (input) { if (input.type !== "submit" && input.type !== "button") { executionContext.arguments.push({ name: input.id, value: this.$(input).val() }); } }, this); this.app.trigger("execute", executionContext); } }); })(diagnostics.module("method")); ================================================ FILE: src/Nancy/Diagnostics/Resources/Modules/interactive/providers.js ================================================ (function (Provider) { var app = diagnostics.app; Provider.Model = Backbone.Model.extend({}); Provider.Collection = Backbone.Collection.extend({ model: Provider.Model, url: function () { return Nancy.config.basePath + "interactive/providers/"; } }); Provider.Views.List = Backbone.View.extend({ el: '#main', initialize: function () { this.template = $("#providerList").html(); }, render: function () { var providers = this.model.toJSON(); var html = Handlebars.compile(this.template)({ collection: providers }); $(this.el).html(html); _.each(providers, this.renderItem, this); }, renderItem: function (model) { var itemView = new Provider.Views.Item({ model: model }); $(this.el).append(itemView.el); } }); Provider.Views.Item = Backbone.View.extend({ className: "item-list", events: { 'click': 'showMethods' }, initialize: function () { this.router = app.router; this.template = $("#provider").html(); this.render(); }, render: function () { var html = Handlebars.compile(this.template)({ model: this.model }); $(this.el).append(html); }, showMethods: function () { this.router.navigate(this.model.name, true); } }); })(diagnostics.module("provider")) ================================================ FILE: src/Nancy/Diagnostics/Resources/Modules/interactive/results.js ================================================ (function (Results, Handlebars, $) { var app = diagnostics.app; Results.Views.Result = Backbone.View.extend({ el: '#results', initialize: function (opts) { this.providerName = opts.providerName; this.methodName = opts.methodName; this.arguments = opts.arguments; this.templatePath = this.providerName + "/" + this.methodName; }, render: function () { $.ajax({ url: Nancy.config.basePath + "interactive/templates/" + this.templatePath, dataType: "text", context: this, success: this.renderWithTemplate, error: this.renderWithoutTemplate }); }, renderWithTemplate: function (template) { var html = Handlebars.compile(template)({ model: this.model }); $(this.el).html(html); }, renderWithoutTemplate: function () { $(this.el).html("
" + _.modelreport(this.model) + "
"); } }); Results.execute = function (executionContext) { console.log("executing: " + executionContext.providerName + "/" + executionContext.methodName); console.log("Arguments:"); _.each(executionContext.arguments, function (arg) { console.log("Name: " + arg.name + " Value:" + arg.value); }); $.ajax({ url: Nancy.config.basePath + "interactive/providers/" + executionContext.providerName + "/" + executionContext.methodName, dataType: "json", data: executionContext.arguments, success: function (data) { var resultsView = new Results.Views.Result({ providerName: executionContext.providerName, methodName: executionContext.methodName, model: data }); resultsView.render(); } }); }; app.bind("execute", Results.execute); })(diagnostics.module("results"), Handlebars, $); ================================================ FILE: src/Nancy/Diagnostics/Resources/Modules/tracing/sessions.js ================================================ (function (Session) { var app = diagnostics.app; Session.Model = Backbone.Model.extend({}); Session.Collection = Backbone.Collection.extend({ model: Session.Model, url: function () { return Nancy.config.basePath + "trace/sessions/"; } }); Session.Views.List = Backbone.View.extend({ el: '#main', initialize: function () { this.template = $("#sessionList").html(); }, render: function () { var sessions = this.model.toJSON(); var html = Handlebars.compile(this.template)({ collection: sessions }); $(this.el).html(html); _.each(sessions, this.renderItem, this); }, renderItem: function (model) { var itemView = new Session.Views.Item({ model: model }); $(this.el).append(itemView.el); } }); Session.Views.Item = Backbone.View.extend({ className: "item-list", events: { 'click': 'showSession' }, initialize: function () { this.router = app.router; this.template = $("#session").html(); this.render(); }, render: function () { var html = Handlebars.compile(this.template)({ model: this.model }); $(this.el).append(html); }, showSession: function () { this.router.navigate(this.model.id, true); } }); })(diagnostics.module("session")) ================================================ FILE: src/Nancy/Diagnostics/Resources/Modules/tracing/traces.js ================================================ (function (Trace) { var app = diagnostics.app; Trace.Model = Backbone.Model.extend({}); Trace.Collection = Backbone.Collection.extend({ model: Trace.Model, initialize: function (opts) { this.id = opts.id; }, url: function () { return Nancy.config.basePath + "trace/sessions/" + this.id; } }); Trace.Views.List = Backbone.View.extend({ el: '#main', events: { 'click #back': 'back' }, initialize: function () { this.router = app.router; this.template = $("#traceList").html(); }, render: function () { var traces = this.model.toJSON(); var html = Handlebars.compile(this.template)({ collection: traces }); $(this.el).html(html); _.each(traces, this.renderItem, this); }, renderItem: function (model) { var itemView = new Trace.Views.Item({ model: model }); this.$('#root').append(itemView.el); }, back: function () { this.router.navigate("", true); } }); Trace.Views.Item = Backbone.View.extend({ className: 'item-list', events: { 'click': 'viewDetails' }, initialize: function (args) { this.template = $("#trace").html(); this.render(); }, render: function () { var html = Handlebars.compile(this.template)({ model: this.model }); $(this.el).append(html); }, viewDetails: function () { var details = new Trace.Views.Details({ model: this.model }); details.render(); } }); Trace.Views.Details = Backbone.View.extend({ el: '#details', render: function () { $(this.el).html("
" + _.modelreport(this.model) + "
"); } }); })(diagnostics.module("trace")); ================================================ FILE: src/Nancy/Diagnostics/Resources/backbone-min.js ================================================ // Backbone.js 0.5.3 // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://documentcloud.github.com/backbone (function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d= 0,e=c.length;d/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute]; var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed= !0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&& c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy", b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!= this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}}); e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow,this.navigate(a); this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({}, document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a); return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(), this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a, b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b= 0,c=n.length;b 0) { for(var i=0, j=context.length; i 0) { for(var i=0, j=context.length; i 2) { expected.push("'"+this.terminals_[p]+"'"); } var errStr = ''; if (this.lexer.showPosition) { errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', '); } else { errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + (symbol == 1 /*EOF*/ ? "end of input" : ("'"+(this.terminals_[symbol] || symbol)+"'")); } this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); } // just recovered from another error if (recovering == 3) { if (symbol == EOF) { throw new Error(errStr || 'Parsing halted.'); } // discard current lookahead and grab another yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; symbol = lex(); } // try to recover from error while (1) { // check for error recovery rule in this state if ((TERROR.toString()) in table[state]) { break; } if (state == 0) { throw new Error(errStr || 'Parsing halted.'); } popStack(1); state = stack[stack.length-1]; } preErrorSymbol = symbol; // save the lookahead token symbol = TERROR; // insert generic error symbol as new lookahead state = stack[stack.length-1]; action = table[state] && table[state][TERROR]; recovering = 3; // allow 3 real symbols to be shifted before reporting a new error } // this shouldn't happen, unless resolve defaults are off if (action[0] instanceof Array && action.length > 1) { throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); } switch (action[0]) { case 1: // shift //this.shiftCount++; stack.push(symbol); vstack.push(this.lexer.yytext); lstack.push(this.lexer.yylloc); stack.push(action[1]); // push state symbol = null; if (!preErrorSymbol) { // normal execution/no error yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; if (recovering > 0) recovering--; } else { // error just occurred, resume old lookahead f/ before error symbol = preErrorSymbol; preErrorSymbol = null; } break; case 2: // reduce //this.reductionCount++; len = this.productions_[action[1]][1]; // perform semantic action yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 // default location, uses first token for firsts, last for lasts yyval._$ = { first_line: lstack[lstack.length-(len||1)].first_line, last_line: lstack[lstack.length-1].last_line, first_column: lstack[lstack.length-(len||1)].first_column, last_column: lstack[lstack.length-1].last_column }; r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); if (typeof r !== 'undefined') { return r; } // pop off stack if (len) { stack = stack.slice(0,-1*len*2); vstack = vstack.slice(0, -1*len); lstack = lstack.slice(0, -1*len); } stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) vstack.push(yyval.$); lstack.push(yyval._$); // goto new state = table[STATE][NONTERMINAL] newState = table[stack[stack.length-2]][stack[stack.length-1]]; stack.push(newState); break; case 3: // accept return true; } } return true; }};/* Jison generated lexer */ var lexer = (function(){ var lexer = ({EOF:1, parseError:function parseError(str, hash) { if (this.yy.parseError) { this.yy.parseError(str, hash); } else { throw new Error(str); } }, setInput:function (input) { this._input = input; this._more = this._less = this.done = false; this.yylineno = this.yyleng = 0; this.yytext = this.matched = this.match = ''; this.conditionStack = ['INITIAL']; this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; return this; }, input:function () { var ch = this._input[0]; this.yytext+=ch; this.yyleng++; this.match+=ch; this.matched+=ch; var lines = ch.match(/\n/); if (lines) this.yylineno++; this._input = this._input.slice(1); return ch; }, unput:function (ch) { this._input = ch + this._input; return this; }, more:function () { this._more = true; return this; }, pastInput:function () { var past = this.matched.substr(0, this.matched.length - this.match.length); return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); }, upcomingInput:function () { var next = this.match; if (next.length < 20) { next += this._input.substr(0, 20-next.length); } return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); }, showPosition:function () { var pre = this.pastInput(); var c = new Array(pre.length + 1).join("-"); return pre + this.upcomingInput() + "\n" + c+"^"; }, next:function () { if (this.done) { return this.EOF; } if (!this._input) this.done = true; var token, match, col, lines; if (!this._more) { this.yytext = ''; this.match = ''; } var rules = this._currentRules(); for (var i=0;i < rules.length; i++) { match = this._input.match(this.rules[rules[i]]); if (match) { lines = match[0].match(/\n.*/g); if (lines) this.yylineno += lines.length; this.yylloc = {first_line: this.yylloc.last_line, last_line: this.yylineno+1, first_column: this.yylloc.last_column, last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} this.yytext += match[0]; this.match += match[0]; this.matches = match; this.yyleng = this.yytext.length; this._more = false; this._input = this._input.slice(match[0].length); this.matched += match[0]; token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]); if (token) return token; else return; } } if (this._input === "") { return this.EOF; } else { this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), {text: "", token: null, line: this.yylineno}); } }, lex:function lex() { var r = this.next(); if (typeof r !== 'undefined') { return r; } else { return this.lex(); } }, begin:function begin(condition) { this.conditionStack.push(condition); }, popState:function popState() { return this.conditionStack.pop(); }, _currentRules:function _currentRules() { return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; }}); lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { var YYSTATE=YY_START switch($avoiding_name_collisions) { case 0: this.begin("mu"); if (yy_.yytext) return 14; break; case 1: return 14; break; case 2: return 24; break; case 3: return 16; break; case 4: return 20; break; case 5: return 19; break; case 6: return 19; break; case 7: return 23; break; case 8: return 23; break; case 9: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.begin("INITIAL"); return 15; break; case 10: return 22; break; case 11: return 34; break; case 12: return 33; break; case 13: return 33; break; case 14: return 36; break; case 15: /*ignore whitespace*/ break; case 16: this.begin("INITIAL"); return 18; break; case 17: this.begin("INITIAL"); return 18; break; case 18: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; break; case 19: return 30; break; case 20: return 30; break; case 21: return 29; break; case 22: return 33; break; case 23: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33; break; case 24: return 'INVALID'; break; case 25: return 5; break; } }; lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^\[.*\]/,/^./,/^$/]; lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":false},"INITIAL":{"rules":[0,1,25],"inclusive":true}};return lexer;})() parser.lexer = lexer; return parser; })(); if (typeof require !== 'undefined' && typeof exports !== 'undefined') { exports.parser = handlebars; exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); } exports.main = function commonjsMain(args) { if (!args[1]) throw new Error('Usage: '+args[0]+' FILE'); if (typeof process !== 'undefined') { var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); } else { var cwd = require("file").path(require("file").cwd()); var source = cwd.join(args[1]).read({charset: "utf-8"}); } return exports.parser.parse(source); } if (typeof module !== 'undefined' && require.main === module) { exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); } }; ; // lib/handlebars/compiler/base.js Handlebars.Parser = handlebars; Handlebars.parse = function(string) { Handlebars.Parser.yy = Handlebars.AST; return Handlebars.Parser.parse(string); }; Handlebars.print = function(ast) { return new Handlebars.PrintVisitor().accept(ast); }; Handlebars.logger = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, // override in the host environment log: function(level, str) {} }; Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); }; ; // lib/handlebars/compiler/ast.js (function() { Handlebars.AST = {}; Handlebars.AST.ProgramNode = function(statements, inverse) { this.type = "program"; this.statements = statements; if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } }; Handlebars.AST.MustacheNode = function(params, hash, unescaped) { this.type = "mustache"; this.id = params[0]; this.params = params.slice(1); this.hash = hash; this.escaped = !unescaped; }; Handlebars.AST.PartialNode = function(id, context) { this.type = "partial"; // TODO: disallow complex IDs this.id = id; this.context = context; }; var verifyMatch = function(open, close) { if(open.original !== close.original) { throw new Handlebars.Exception(open.original + " doesn't match " + close.original); } }; Handlebars.AST.BlockNode = function(mustache, program, close) { verifyMatch(mustache.id, close); this.type = "block"; this.mustache = mustache; this.program = program; }; Handlebars.AST.InverseNode = function(mustache, program, close) { verifyMatch(mustache.id, close); this.type = "inverse"; this.mustache = mustache; this.program = program; }; Handlebars.AST.ContentNode = function(string) { this.type = "content"; this.string = string; }; Handlebars.AST.HashNode = function(pairs) { this.type = "hash"; this.pairs = pairs; }; Handlebars.AST.IdNode = function(parts) { this.type = "ID"; this.original = parts.join("."); var dig = [], depth = 0; for(var i=0,l=parts.length; i": ">", '"': """, "'": "'", "`": "`" }; var badChars = /&(?!\w+;)|[<>"'`]/g; var possible = /[&<>"'`]/; var escapeChar = function(chr) { return escape[chr] || "&"; }; Handlebars.Utils = { escapeExpression: function(string) { // don't escape SafeStrings, since they're already safe if (string instanceof Handlebars.SafeString) { return string.toString(); } else if (string == null || string === false) { return ""; } if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); }, isEmpty: function(value) { if (typeof value === "undefined") { return true; } else if (value === null) { return true; } else if (value === false) { return true; } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { return true; } else { return false; } } }; })();; // lib/handlebars/compiler/compiler.js Handlebars.Compiler = function() {}; Handlebars.JavaScriptCompiler = function() {}; (function(Compiler, JavaScriptCompiler) { Compiler.OPCODE_MAP = { appendContent: 1, getContext: 2, lookupWithHelpers: 3, lookup: 4, append: 5, invokeMustache: 6, appendEscaped: 7, pushString: 8, truthyOrFallback: 9, functionOrFallback: 10, invokeProgram: 11, invokePartial: 12, push: 13, assignToHash: 15, pushStringParam: 16 }; Compiler.MULTI_PARAM_OPCODES = { appendContent: 1, getContext: 1, lookupWithHelpers: 2, lookup: 1, invokeMustache: 3, pushString: 1, truthyOrFallback: 1, functionOrFallback: 1, invokeProgram: 3, invokePartial: 1, push: 1, assignToHash: 1, pushStringParam: 1 }; Compiler.DISASSEMBLE_MAP = {}; for(var prop in Compiler.OPCODE_MAP) { var value = Compiler.OPCODE_MAP[prop]; Compiler.DISASSEMBLE_MAP[value] = prop; } Compiler.multiParamSize = function(code) { return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]]; }; Compiler.prototype = { compiler: Compiler, disassemble: function() { var opcodes = this.opcodes, opcode, nextCode; var out = [], str, name, value; for(var i=0, l=opcodes.length; i 0) { this.source[1] = this.source[1] + ", " + locals.join(", "); } // Generate minimizer alias mappings if (!this.isChild) { var aliases = [] for (var alias in this.context.aliases) { this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; } } if (this.source[1]) { this.source[1] = "var " + this.source[1].substring(2) + ";"; } // Merge children if (!this.isChild) { this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; } if (!this.environment.isSimple) { this.source.push("return buffer;"); } var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } return "stack" + this.stackSlot; }, popStack: function() { return "stack" + this.stackSlot--; }, topStack: function() { return "stack" + this.stackSlot; }, quotedString: function(str) { return '"' + str .replace(/\\/g, '\\\\') .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') + '"'; } }; var reservedWords = ("break case catch continue default delete do else finally " + "for function if in instanceof new return switch this throw " + "try typeof var void while with null true false").split(" "); var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; for(var i=0, l=reservedWords.length; i
' + val(m[k]) + '
'; sb += ''; return sb; } function arr(m) { if (typeof m[0] == 'string' || typeof m[0] == 'number') return m.join('
'); var id = tbls.length, h = uniqueKeys(m); var sb = ''; tbls.push(m); var i = 0; for (var k in h) sb += ''; sb += '' + makeRows(h, m) + '
' + splitCase(k) + '
'; return sb; } function makeRows(h, m) { var sb = ''; for (var r = 0, len = m.length; r < len; r++) { sb += ''; var row = m[r]; for (var k in h) sb += '' + val(row[k]) + ''; sb += ''; } return sb; } function setTableBody(tbody, html) { if (!isIE) { tbody.innerHTML = html; return; } var temp = tbody.ownerDocument.createElement('div'); temp.innerHTML = '' + html + '
'; tbody.parentNode.replaceChild(temp.firstChild.firstChild, tbody); } function clearSel() { if (doc.selection && doc.selection.empty) doc.selection.empty(); else if (root.getSelection) { var sel = root.getSelection(); if (sel && sel.removeAllRanges) sel.removeAllRanges(); } } function cmp(v1, v2) { var f1, f2, f1 = parseFloat(v1), f2 = parseFloat(v2); if (!isNaN(f1) && !isNaN(f2)) v1 = f1, v2 = f2; if (typeof v1 == 'string' && v1.substr(0, 6) == '/Date(') v1 = date(v1), v2 = date(v2); if (v1 == v2) return 0; return v1 > v2 ? 1 : -1; } function enc(html) { if (typeof html != 'string') return html; return html.replace(//g, '>').replace(/"/g, '"'); } function addEvent(obj, type, fn) { if (obj.attachEvent) { obj['e' + type + fn] = fn; obj[type + fn] = function () { obj['e' + type + fn](root.event); } obj.attachEvent('on' + type, obj[type + fn]); } else obj.addEventListener(type, fn, false); } addEvent(doc, 'click', function (e) { var e = e || root.event, el = e.target || e.srcElement, cls = el.className; if (el.tagName == 'B') el = el.parentNode; if (el.tagName != 'TH') return; el.className = cls == 'asc' ? 'desc' : (cls == 'desc' ? null : 'asc'); $.each($$('TH'), function (i, th) { if (th == el) return; th.className = null; }); clearSel(); var ids = el.id.split('-'), tId = ids[1], cId = ids[2]; var tbl = tbls[tId].slice(0), h = uniqueKeys(tbl), col = keys(h)[cId], tbody = el.parentNode.parentNode.nextSibling; if (!el.className) { setTableBody(tbody, makeRows(h, tbls[tId])); return; } var d = el.className == 'asc' ? 1 : -1; tbl.sort(function (a, b) { return cmp(a[col], b[col]) * d; }); setTableBody(tbody, makeRows(h, tbl)); }); return function (model) { return val(model); }; })(); ================================================ FILE: src/Nancy/Diagnostics/Resources/main.css ================================================ body { background: black; color: white; font-family: Arial; } ul { list-style: none; } button.g-button, a.g-button, input[type=submit].g-button, input[type=button].g-button { padding: 6px 10px; -webkit-border-radius: 2px 2px; border: solid 1px rgb(153, 153, 153); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 255, 255)), to(rgb(221, 221, 221))); color: #333; text-decoration: none; cursor: pointer; display: inline-block; text-align: center; text-shadow: 0px 1px 1px rgba(255,255,255,1); line-height: 1; } .container_12 { padding: 20px 0; position: relative; } .header { padding: 0 0 28px 0; height: 100px; position: relative; } .header h1 { font-size: 24pt; position: absolute; bottom: 0; right: 0; margin-bottom: 18px; } a.icon-block { display: block; background-color: white; height: 180px; line-height: 0; font-size: 0; color: transparent; } a.icon-block:hover { border: 2px solid #555555; height: 176px; margin-right: 8px; margin-left: 8px; background-size: auto 150px; } div.description-text h2 { font-size: 14pt; font-weight: bold; margin-bottom: 0px; margin-top: 5px; } #infoBox { background-image: url(info.png); background-position: center center; background-repeat: no-repeat; } #interactiveBox { background-image: url(interactive.png); background-position: center center; background-repeat: no-repeat; } #traceBox { background-image: url(logs.png); background-position: center center; background-repeat: no-repeat; } #settingsBox { background-image: url(settings.png); background-position: center center; background-repeat: no-repeat; } div.item-list { background: white; color: Black; width: 95%; padding: 7px; margin-bottom: 5px; min-height: 100px; cursor: pointer; } div.item-list:hover { padding: 5px; border: 2px solid #555555; } div.item-list h2 { font-size: 14pt; font-weight: bold; margin-bottom: 0px; } #back { cursor: pointer; font-size: 120%; margin-bottom: 10px; display: block; } /* JSON Report CSS */ .ib { text-transform: capitalize; } .modelreport TABLE { border-collapse: collapse; border: solid 1px #ccc; clear: left; } .modelreport TH { text-align: left; padding: 4px 8px; text-shadow: #fff 1px 1px -1px; background: #f1f1f1; color: black; white-space: nowrap; cursor: pointer; font-weight: bold; } .modelreport TH, .modelreport TD, .modelreport TD DT, .modelreport TD DD { font-size: 11px; font-family: Arial; } .modelreport TD { padding: 8px 8px 0 8px; vertical-align: top; } .modelreport DL { clear: left; margin: 0px; } .modelreport DT { /* margin: 10px 0 5px 0;*/ font-weight: bold; font-size: 13px; width: 260px; clear: left; float: left; display: block; white-space: nowrap; overflow: hidden; } .modelreport DD { /* margin: 5px 10px;*/ font-weight: bold; font-size: 13px; display: block; float: left; } .modelreport DL DL DT { font-weight: bold; font-size: 13px; } .modelreport DL DL DD { font-size: 13px; max-width: 200px; } .modelreport HR { display: none; } .modelreport TD DL HR { display: block; padding: 0; clear: left; border: none; } .modelreport TD DL { padding: 4px; margin: 0; height: 100%; max-width: 700px; } .modelreport DL TD DL DT { padding: 2px; margin: 0 10px 0 0; font-weight: bold; font-size: 11px; width: 120px; overflow: hidden; clear: left; float: left; display: block; } .modelreport DL TD DL DD { margin: 0; padding: 2px; font-size: 8px; display: block; float: left; } .modelreport TBODY > TR:last-child > TD { padding: 8px; } .modelreport THEAD { -webkit-user-select: none; -moz-user-select: none; } .modelreport .desc, .modelreport .asc { background-color: #FAFAD2; } .modelreport .desc { background-color: #D4EDC9; } .modelreport TH B { display: block; float: right; margin: 0 0 0 5px; width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #ccc; border-bottom: none; } .modelreport .asc B { border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #333; border-bottom: none; } .modelreport .desc B { border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 5px solid #333; border-top: none; } .modelreport H3 { font-size: 13px; margin: 0 0 10px 0; } /* Settings */ #settings { float: left; } #settings li { margin-bottom: 10px; } .help p { font-size: 14px; } ================================================ FILE: src/Nancy/Diagnostics/Resources/nancy-common.js ================================================  ================================================ FILE: src/Nancy/Diagnostics/Resources/request-tracing.js ================================================ $(function () { var app = diagnostics.app; var Session = diagnostics.module("session"); var Trace = diagnostics.module("trace"); var Router = Backbone.Router.extend({ routes: { "": "index", ":id": "trace" }, index: function () { var sessions = new Session.Collection(); sessions.fetch().success(function () { var list = new Session.Views.List({ model: sessions }); list.render(); }); }, trace: function (id) { var traces = new Trace.Collection({ id: id }); traces.fetch().success(function () { var list = new Trace.Views.List({ model: traces }); list.render(); }); } }); // Start router and trigger first route app.router = new Router(); Backbone.history.start(); }); ================================================ FILE: src/Nancy/Diagnostics/Resources/reset.css ================================================ a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,dialog,div,dl,dt,em,embed,fieldset,figcaption,figure,font,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,meter,nav,object,ol,output,p,pre,progress,q,rp,rt,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video,xmp{border:0;margin:0;padding:0;font-size:100%}html,body{height:100%}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}b,strong{font-weight:bold}img{color:transparent;font-size:0;vertical-align:middle;-ms-interpolation-mode:bicubic}ol,ul{list-style:none}li{display:list-item}table{border-collapse:collapse;border-spacing:0}th,td,caption{font-weight:normal;vertical-align:top;text-align:left}q{quotes:none}q:before,q:after{content:'';content:none}sub,sup,small{font-size:75%}sub,sup{line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}svg{overflow:hidden} ================================================ FILE: src/Nancy/Diagnostics/Resources/text.css ================================================ body{font:13px/1.5 'Helvetica Neue',Arial,'Liberation Sans',FreeSans,sans-serif}pre,code{font-family:'DejaVu Sans Mono',Menlo,Consolas,monospace}hr{border:0 #ccc solid;border-top-width:1px;clear:both;height:0}h1{font-size:25px}h2{font-size:23px}h3{font-size:21px}h4{font-size:19px}h5{font-size:17px}h6{font-size:15px}ol{list-style:decimal}ul{list-style:disc}li{margin-left:30px}p,dl,hr,h1,h2,h3,h4,h5,h6,ol,ul,pre,table,address,fieldset,figure{margin-bottom:20px} ================================================ FILE: src/Nancy/Diagnostics/Resources/underscore-min.js ================================================ // Underscore.js 1.2.2 // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: // http://documentcloud.github.com/underscore (function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(b.isFunction(a.isEqual))return a.isEqual(c);if(b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return String(a)==String(c);case "[object Number]":return a=+a,c=+c,a!=a?c!=c:a==0?1/a==1/c:a==c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c, h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,H=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&&define.amd? define("underscore",function(){return b}):s._=b;b.VERSION="1.2.2";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,c){var b=e(a,c);(d[b]||(d[b]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e< f;){var g=e+f>>1;d(a[g])=0})})};b.difference=function(a,c){return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=H||function(a){if(a!== Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)? a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=l.call(arguments)=="[object Arguments]"?function(a){return l.call(a)=="[object Arguments]"}: function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null}; b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),function(c){I(c,b[c]=a[c])})};var J=0;b.uniqueId=function(a){var b=J++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g, interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g, "\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e(a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},I=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);G.call(a,this._wrapped);return u(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped, arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this); ================================================ FILE: src/Nancy/Diagnostics/ResponseData.cs ================================================ namespace Nancy.Diagnostics { using System; using System.Collections.Generic; /// /// Stores request trace information about the response. /// public class ResponseData { /// /// Gets or sets the content type of the response. /// /// A containing the content type. public string ContentType { get; set; } /// /// Gets or sets the headers of the response. /// /// A instance containing the headers. public IDictionary Headers { get; set; } /// /// Gets or sets the of the response. /// public HttpStatusCode StatusCode { get; set; } /// /// Gets or sets the of the response. /// /// A instance. public Type Type { get; set; } /// /// Implicitly casts a instance into a instance. /// /// A instance. /// A instance. public static implicit operator ResponseData(Response response) { return new ResponseData { ContentType = response.ContentType, Headers = response.Headers, StatusCode = response.StatusCode, Type = response.GetType() }; } } } ================================================ FILE: src/Nancy/Diagnostics/TemplateAttribute.cs ================================================ namespace Nancy.Diagnostics { using System; /// /// Attribute for defining an HTML template. /// /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class TemplateAttribute : Attribute { /// /// Gets or sets the template. /// /// The template as . public string Template { get; set; } /// /// Initializes a new instance of the class. /// /// The template as . public TemplateAttribute(string template) { this.Template = template; } } } ================================================ FILE: src/Nancy/Diagnostics/TestingDiagnosticProvider.cs ================================================ namespace Nancy.Diagnostics { /// /// A dummy diagnostic provider for testing purposes. /// /// public class TestingDiagnosticProvider : IDiagnosticsProvider { private readonly object diagObject; /// /// Initializes a new instance of the class. /// public TestingDiagnosticProvider() { this.diagObject = new DiagObject(); } /// /// Gets the name of the provider. /// /// A containing the name of the provider. public string Name { get { return "Testing Diagnostic Provider"; } } /// /// Gets the description of the provider. /// /// A containing the description of the provider. public string Description { get { return "Some testing methods that can be called to.. erm.. test things."; } } /// /// Gets the object that contains the interactive diagnostics methods. /// /// An instance of the interactive diagnostics object. public object DiagnosticObject { get { return this.diagObject; } } /// /// Contains dummy diagnostic methods. /// public class DiagObject { /// /// Empty return value /// public void NoReturnValue() { } /// /// String return value. /// /// A dummy value. public string StringReturnValue() { return "Hello!"; } /// /// Returns hello with a given name. /// /// A name. /// A dummy value. public string SayHello(string name) { return string.Format("Hello {0}!", name); } /// /// Returns the template for the SayHelloWithAge /// /// The template for the SayHelloWithAge public string SayHelloWithAgeTemplate { get { return "

Templated Results

{{model.result}}

"; } } /// /// Returns the description of the SayHelloWithAgeDescription method /// /// Description for the test method public string SayHelloWithAgeDescription { get { return "Simple test method that takes a name and an age and returns a result with a template."; } } /// /// Simple test method that takes a name and an age and returns a string. /// /// A name. /// An age. /// A string with the given name and age. public string SayHelloWithAge(string myName, int myAge) { return string.Format("Hello {0}, you are {1} years old!", myName, myAge); } /// /// Returns a string with a name and an age using built-in attributes. /// /// My name. /// My age. /// A templated string with the given name and age. [Template("

Templated Results

{{model.result}}

")] [Description("Simple test method that takes a name and an age and returns a result with a template.")] public string SayHelloWithAge2(string myName, int myAge) { return string.Format("Hello {0}, you are {1} years old!", myName, myAge); } } } } ================================================ FILE: src/Nancy/Diagnostics/Views/Dashboard.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Dashboard@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Dashboard

@EndSection @Section['Body']

Information

Information about Nancy's current runtime configuration. A useful starting point for diagnosing a misbehaving site.

Interactive Diagnostics

Interactive diagnostics allow you to poke and prod at the guts of your Nancy site, from querying configuration, to clearing caches, through to executing parts of the pipeline and visualising the results.

Request Tracing

Provides tracing information about requests including request/response headers and a tracelog of each request.

Settings

Configure some of Nancy's runtime static configuration.

@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/Info.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Info@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Info

@EndSection @Section['Body']
@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/InteractiveDiagnostics.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Interactive Diagnostics@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Interactive Diagnostics

@EndSection @Section['Body']
@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/RequestTracing.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Request Tracing@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Request Tracing

@EndSection @Section['Body']
@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/Settings.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Settings@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Settings

@EndSection @Section['Body']
    @Each

  • @Current.Description
  • @EndEach
@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/_DiagnosticsMaster.sshtml ================================================  @Section['Title']; @Section['Header'];
@Section['Page_Title'];
@Section['Body'];
================================================ FILE: src/Nancy/Diagnostics/Views/help.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Diagnostics Disabled@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Diagnostics Disabled

@EndSection @Section['Body']
 

Diagnostics is currently not correctly configured for this website. Please review your diagnostic configuration and try again.

 
@EndSection ================================================ FILE: src/Nancy/Diagnostics/Views/login.sshtml ================================================ @Master['_DiagnosticsMaster'] @Section['Title']Login@EndSection @Section['Header'] @EndSection @Section['Page_Title']

Login

@EndSection @Section['Body']
 
Password:
 
@EndSection ================================================ FILE: src/Nancy/DisabledStaticContentProvider.cs ================================================ namespace Nancy { /// /// A "disabled" static content provider - always returns null /// so no content is served. /// public class DisabledStaticContentProvider : IStaticContentProvider { /// /// Gets the static content response, if possible. /// /// Current context /// Response if serving content, null otherwise public Response GetContent(NancyContext context) { return null; } } } ================================================ FILE: src/Nancy/DynamicDictionary.cs ================================================ namespace Nancy { using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.Linq; using System.Text; /// /// A dictionary that supports dynamic access. /// [DebuggerDisplay("{DebuggerDisplay, nq}")] public class DynamicDictionary : DynamicObject, IEquatable, IHideObjectMembers, IEnumerable, IDictionary { private readonly GlobalizationConfiguration globalizationConfiguration; private readonly IDictionary dictionary = new Dictionary(StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase); /// /// Initializes a new istance of the class. /// public DynamicDictionary() : this(GlobalizationConfiguration.Default) { } /// /// Initializes a new istance of the class. /// /// A instance. public DynamicDictionary(GlobalizationConfiguration globalizationConfiguration) { this.globalizationConfiguration = globalizationConfiguration; } /// /// Returns an empty dynamic dictionary. /// /// A instance. public static DynamicDictionary Empty { get { return new DynamicDictionary(); } } /// /// Creates a dynamic dictionary from an instance. /// /// An instance, that the dynamic dictionary should be created from. /// /// An instance. public static DynamicDictionary Create(IDictionary values, GlobalizationConfiguration globalizationConfiguration) { var instance = new DynamicDictionary(globalizationConfiguration); foreach (var key in values.Keys) { instance[key] = values[key]; } return instance; } /// /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". public override bool TrySetMember(SetMemberBinder binder, object value) { this[binder.Name] = value; return true; } /// /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.) /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.The result of the get operation. For example, if the method is called for a property, you can assign the property value to . public override bool TryGetMember(GetMemberBinder binder, out object result) { if (!this.dictionary.TryGetValue(binder.Name, out result)) { result = new DynamicDictionaryValue(null, this.globalizationConfiguration); } return true; } /// /// Returns the enumeration of all dynamic member names. /// /// A that contains dynamic member names. public override IEnumerable GetDynamicMemberNames() { return this.dictionary.Keys; } /// /// Returns the enumeration of all dynamic member names. /// /// A that contains dynamic member names. public IEnumerator GetEnumerator() { return this.dictionary.Keys.GetEnumerator(); } /// /// Returns the enumeration of all dynamic member names. /// /// A that contains dynamic member names. IEnumerator IEnumerable.GetEnumerator() { return this.dictionary.Keys.GetEnumerator(); } /// /// Gets or sets the with the specified name. /// /// A instance containing a value. public dynamic this[string name] { get { name = GetNeutralKey(name); dynamic member; if (!this.dictionary.TryGetValue(name, out member)) { member = new DynamicDictionaryValue(null, this.globalizationConfiguration); } return member; } set { name = GetNeutralKey(name); this.dictionary[name] = value is DynamicDictionaryValue ? value : new DynamicDictionaryValue(value, this.globalizationConfiguration); } } /// /// Indicates whether the current is equal to another object of the same type. /// /// if the current instance is equal to the parameter; otherwise, . /// An instance to compare with this instance. public bool Equals(DynamicDictionary other) { if (ReferenceEquals(null, other)) { return false; } return ReferenceEquals(this, other) || Equals(other.dictionary, this.dictionary); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// if the specified is equal to this instance; otherwise, . public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } return obj.GetType() == typeof(DynamicDictionary) && this.Equals((DynamicDictionary)obj); } /// /// Returns an enumerator that iterates through the collection. /// /// A that can be used to iterate through the collection. IEnumerator> IEnumerable>.GetEnumerator() { return this.dictionary.GetEnumerator(); } /// /// Returns a hash code for this . /// /// A hash code for this , suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() { return (this.dictionary != null ? this.dictionary.GetHashCode() : 0); } /// /// Adds an element with the provided key and value to the . /// /// The object to use as the key of the element to add. /// The object to use as the value of the element to add. public void Add(string key, dynamic value) { this[key] = value; } /// /// Adds an item to the . /// /// The object to add to the . public void Add(KeyValuePair item) { this[item.Key] = item.Value; } /// /// Determines whether the contains an element with the specified key. /// /// if the contains an element with the key; otherwise, . /// /// The key to locate in the . public bool ContainsKey(string key) { key = GetNeutralKey(key); return this.dictionary.ContainsKey(key); } /// /// Gets an containing the keys of the . /// /// An containing the keys of the . public ICollection Keys { get { return this.dictionary.Keys; } } /// /// Gets the value associated with the specified key. /// /// if the contains an element with the specified key; otherwise, . /// 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 parameter. This parameter is passed uninitialized. public bool TryGetValue(string key, out dynamic value) { key = GetNeutralKey(key); return this.dictionary.TryGetValue(key, out value); } /// /// Removes all items from the . /// public void Clear() { this.dictionary.Clear(); } /// /// Gets the number of elements contained in the . /// /// The number of elements contained in the . public int Count { get { return this.dictionary.Count; } } /// /// Determines whether the contains a specific value. /// /// if is found in the ; otherwise, . /// /// The object to locate in the . public bool Contains(KeyValuePair item) { var dynamicValueKeyValuePair = this.GetDynamicKeyValuePair(item); return this.dictionary.Contains(dynamicValueKeyValuePair); } /// /// Copies the elements of the to an , starting at a particular index. /// /// The one-dimensional that is the destination of the elements copied from the . The must have zero-based indexing. /// The zero-based index in at which copying begins. public void CopyTo(KeyValuePair[] array, int arrayIndex) { this.dictionary.CopyTo(array, arrayIndex); } /// /// Gets a value indicating whether the is read-only. /// /// Always returns . public bool IsReadOnly { get { return false; } } /// /// Removes the element with the specified key from the . /// /// if the element is successfully removed; otherwise, . /// The key of the element to remove. public bool Remove(string key) { key = GetNeutralKey(key); return this.dictionary.Remove(key); } /// /// Removes the first occurrence of a specific object from the . /// /// if was successfully removed from the ; otherwise, . /// The object to remove from the . public bool Remove(KeyValuePair item) { var dynamicValueKeyValuePair = this.GetDynamicKeyValuePair(item); return this.dictionary.Remove(dynamicValueKeyValuePair); } /// /// Gets an containing the values in the . /// /// An containing the values in the . public ICollection Values { get { return this.dictionary.Values; } } private KeyValuePair GetDynamicKeyValuePair(KeyValuePair item) { var dynamicValueKeyValuePair = new KeyValuePair(item.Key, new DynamicDictionaryValue(item.Value, this.globalizationConfiguration)); return dynamicValueKeyValuePair; } private static string GetNeutralKey(string key) { return key.Replace("-", string.Empty); } /// /// Gets a typed Dictionary of from /// /// Gets a typed Dictionary of from public Dictionary ToDictionary() { var data = new Dictionary(); foreach (var item in this.dictionary) { var newKey = item.Key; var newValue = ((DynamicDictionaryValue)item.Value).Value; data.Add(newKey, newValue); } return data; } private string DebuggerDisplay { get { var builder = new StringBuilder(); var maxItems = Math.Min(this.dictionary.Count, 5); builder.Append("{"); for (var i = 0; i < maxItems; i++) { var item = this.dictionary.ElementAt(i); builder.AppendFormat(" {0} = {1}{2}", item.Key, item.Value, i < maxItems - 1 ? "," : string.Empty); } if (maxItems < this.dictionary.Count) { builder.Append("..."); } builder.Append(" }"); return builder.ToString(); } } } } ================================================ FILE: src/Nancy/DynamicDictionaryValue.cs ================================================ namespace Nancy { using System; using System.ComponentModel; using System.Dynamic; using System.Globalization; using System.Linq.Expressions; using System.Reflection; using Microsoft.CSharp.RuntimeBinder; using Extensions; using Binder = Microsoft.CSharp.RuntimeBinder.Binder; /// /// A value that is stored inside a instance. /// public class DynamicDictionaryValue : DynamicObject, IEquatable, IHideObjectMembers, IConvertible { private readonly object value; private readonly GlobalizationConfiguration globalizationConfiguration; /// /// Initializes a new instance of the class, with /// the provided . /// /// The value to store in the instance public DynamicDictionaryValue(object value) : this(value, GlobalizationConfiguration.Default) { } /// /// Initializes a new instance of the class, with /// the provided and . /// /// The value to store in the instance /// A instance. public DynamicDictionaryValue(object value, GlobalizationConfiguration globalizationConfiguration) { this.value = value; this.globalizationConfiguration = globalizationConfiguration; } /// /// Gets a value indicating whether this instance has value. /// /// if this instance has value; otherwise, . /// is considered as not being a value. public bool HasValue { get { return (this.value != null); } } /// /// Gets the inner value /// public object Value { get { return this.value; } } /// /// Returns a default value if Value is null /// /// When no default value is supplied, required to supply the default type /// Optional parameter for default value, if not given it returns default of type T /// If value is not null, value is returned, else default value is returned public T Default(T defaultValue = default(T)) { if (this.HasValue) { try { return (T)this.value; } catch { var typeName = this.value.GetType().Name; var message = string.Format("Cannot convert value of type '{0}' to type '{1}'", typeName, typeof(T).Name); throw new InvalidCastException(message); } } return defaultValue; } /// /// Attempts to convert the value to type of T, failing to do so will return the defaultValue. /// /// When no default value is supplied, required to supply the default type /// Optional parameter for default value, if not given it returns default of type T /// If value is not null, value is returned, else default value is returned public T TryParse(T defaultValue = default (T)) { if (this.HasValue) { try { var valueType = this.value.GetType(); var parseType = typeof(T); // check for direct cast if (valueType.IsAssignableFrom(parseType)) { return (T)this.value; } var stringValue = this.value as string; if (parseType == typeof(DateTime)) { DateTime result; if (DateTime.TryParse(stringValue, CultureInfo.InvariantCulture, this.globalizationConfiguration.DateTimeStyles, out result)) { return (T)((object)result); } return defaultValue; } if (stringValue != null) { var converter = TypeDescriptor.GetConverter(parseType); if (converter.CanConvertFrom(typeof(string))) { return (T) converter.ConvertFromInvariantString(stringValue); } return defaultValue; } var underlyingType = Nullable.GetUnderlyingType(parseType) ?? parseType; return (T)Convert.ChangeType(this.value, underlyingType, CultureInfo.InvariantCulture); } catch { return defaultValue; } } return defaultValue; } /// /// == operator for /// /// /// value to compare to /// if equal, otherwise public static bool operator ==(DynamicDictionaryValue dynamicValue, object compareValue) { if (ReferenceEquals(null, dynamicValue)) { return false; } if (dynamicValue.value == null && compareValue == null) { return true; } return dynamicValue.value != null && dynamicValue.value.Equals(compareValue); } /// /// != operator for /// /// /// value to compare to /// if not equal, otherwise public static bool operator !=(DynamicDictionaryValue dynamicValue, object compareValue) { return !(dynamicValue == compareValue); } /// /// Indicates whether the current object is equal to another object of the same type. /// /// if the current object is equal to the parameter; otherwise, . /// /// An to compare with this instance. public bool Equals(DynamicDictionaryValue compareValue) { if (ReferenceEquals(null, compareValue)) { return false; } return ReferenceEquals(this, compareValue) || Equals(compareValue.value, this.value); } /// /// Determines whether the specified is equal to the current . /// /// if the specified is equal to the current ; otherwise, . /// The to compare with the current . public override bool Equals(object compareValue) { if (ReferenceEquals(null, compareValue)) { return false; } if (ReferenceEquals(this, compareValue)) { return true; } return compareValue.GetType() == typeof(DynamicDictionaryValue) && this.Equals((DynamicDictionaryValue)compareValue); } /// /// Serves as a hash function for a particular type. /// /// A hash code for the current instance. public override int GetHashCode() { return (this.value != null ? this.value.GetHashCode() : 0); } /// /// Provides implementation for binary operations. Classes derived from the class can override this method to specify dynamic behavior for operations such as addition and multiplication. /// /// if the operation is successful; otherwise, . If this method returns , the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// Provides information about the binary operation. The binder.Operation property returns an object. For example, for the sum = first + second statement, where first and second are derived from the DynamicObject class, binder.Operation returns ExpressionType.Add.The right operand for the binary operation. For example, for the sum = first + second statement, where first and second are derived from the DynamicObject class, is equal to second.The result of the binary operation. public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) { object resultOfCast; result = null; if (binder.Operation != ExpressionType.Equal) { return false; } var convert = Binder.Convert(CSharpBinderFlags.None, arg.GetType(), typeof(DynamicDictionaryValue)); if (!this.TryConvert((ConvertBinder)convert, out resultOfCast)) { return false; } result = (resultOfCast == null) ? Equals(arg, resultOfCast) : resultOfCast.Equals(arg); return true; } /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// /// if the operation is successful; otherwise, . If this method returns , the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.The result of the type conversion operation. public override bool TryConvert(ConvertBinder binder, out object result) { result = null; if (this.value == null) { return true; } var binderType = binder.Type; if (binderType == typeof(string)) { result = Convert.ToString(this.value); return true; } if (binderType == typeof(Guid) || binderType == typeof(Guid?)) { Guid guid; if (Guid.TryParse(Convert.ToString(this.value), out guid)) { result = guid; return true; } } else if (binderType == typeof(TimeSpan) || binderType == typeof(TimeSpan?)) { TimeSpan timespan; if (TimeSpan.TryParse(Convert.ToString(this.value), out timespan)) { result = timespan; return true; } } else if (binderType.GetTypeInfo().IsEnum) { // handles enum to enum assignments if (this.value.GetType().GetTypeInfo().IsEnum) { if (binderType == this.value.GetType()) { result = this.value; return true; } return false; } // handles number to enum assignments if (Enum.GetUnderlyingType(binderType) == this.value.GetType()) { result = Enum.ToObject(binderType, this.value); return true; } return false; } else { if (binderType.GetTypeInfo().IsGenericType && binderType.GetGenericTypeDefinition() == typeof(Nullable<>)) { binderType = binderType.GetGenericArguments()[0]; } var typeCode = binderType.GetTypeCode(); if (typeCode == TypeCode.Object) { if (binderType.IsAssignableFrom(this.value.GetType())) { result = this.value; return true; } else { return false; } } #if !NETSTANDARD1_6 result = Convert.ChangeType(this.value, typeCode); #else result = Convert.ChangeType(this.value, binderType); #endif return true; } return base.TryConvert(binder, out result); } /// /// Returns a that represents a instance. /// /// /// A that represents this instance. /// public override string ToString() { return this.value == null ? base.ToString() : Convert.ToString(this.value); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator bool?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(bool?); } return (bool)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator bool(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return false; } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return (Convert.ToBoolean(dynamicValue.value)); } bool result; if (bool.TryParse(dynamicValue.ToString(), out result)) { return result; } return true; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator string(DynamicDictionaryValue dynamicValue) { return dynamicValue.HasValue ? Convert.ToString(dynamicValue.value) : null; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator int?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(int?); } return (int)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator int(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(int); } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return Convert.ToInt32(dynamicValue.value); } return int.Parse(dynamicValue.ToString()); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator Guid?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(Guid?); } return (Guid)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator Guid(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(Guid); } if (dynamicValue.value is Guid) { return (Guid)dynamicValue.value; } return Guid.Parse(dynamicValue.ToString()); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator DateTime?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(DateTime?); } return (DateTime)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator DateTime(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(DateTime); } if (dynamicValue.value is DateTime) { return (DateTime)dynamicValue.value; } return DateTime.Parse(dynamicValue.ToString(), CultureInfo.InvariantCulture, dynamicValue.globalizationConfiguration.DateTimeStyles); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator TimeSpan?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(TimeSpan?); } return (TimeSpan)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator TimeSpan(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(TimeSpan); } if (dynamicValue.value is TimeSpan) { return (TimeSpan)dynamicValue.value; } return TimeSpan.Parse(dynamicValue.ToString()); } /// /// Implicit type conversion operator from to long? /// /// public static implicit operator long?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(long?); } return (long)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator long(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(long); } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return Convert.ToInt64(dynamicValue.value); } return long.Parse(dynamicValue.ToString()); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator float?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(float?); } return (float)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator float(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(float); } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return Convert.ToSingle(dynamicValue.value); } return float.Parse(dynamicValue.ToString()); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator decimal?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(decimal?); } return (decimal)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator decimal(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(decimal); } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return Convert.ToDecimal(dynamicValue.value); } return decimal.Parse(dynamicValue.ToString()); } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator double?(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(double?); } return (double)dynamicValue; } /// /// Performs an implicit conversion from to . /// /// The instance /// /// The result of the conversion. /// public static implicit operator double(DynamicDictionaryValue dynamicValue) { if (!dynamicValue.HasValue) { return default(double); } if (dynamicValue.value.GetType().GetTypeInfo().IsValueType) { return Convert.ToDouble(dynamicValue.value); } return double.Parse(dynamicValue.ToString()); } #region Implementation of IConvertible /// /// Returns the for this instance. /// /// /// The enumerated constant that is the of the class or value type that implements this interface. /// /// 2 public TypeCode GetTypeCode() { if (this.value == null) { return TypeCode.Empty; } return this.value.GetType().GetTypeCode(); } /// /// Converts the value of this instance to an equivalent Boolean value using the specified culture-specific formatting information. /// /// /// A Boolean value equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public bool ToBoolean(IFormatProvider provider) { return Convert.ToBoolean(this.value, provider); } /// /// Converts the value of this instance to an equivalent Unicode character using the specified culture-specific formatting information. /// /// /// A Unicode character equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public char ToChar(IFormatProvider provider) { return Convert.ToChar(this.value, provider); } /// /// Converts the value of this instance to an equivalent 8-bit signed integer using the specified culture-specific formatting information. /// /// /// An 8-bit signed integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public sbyte ToSByte(IFormatProvider provider) { return Convert.ToSByte(this.value, provider); } /// /// Converts the value of this instance to an equivalent 8-bit unsigned integer using the specified culture-specific formatting information. /// /// /// An 8-bit unsigned integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public byte ToByte(IFormatProvider provider) { return Convert.ToByte(this.value, provider); } /// /// Converts the value of this instance to an equivalent 16-bit signed integer using the specified culture-specific formatting information. /// /// /// An 16-bit signed integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public short ToInt16(IFormatProvider provider) { return Convert.ToInt16(this.value, provider); } /// /// Converts the value of this instance to an equivalent 16-bit unsigned integer using the specified culture-specific formatting information. /// /// /// An 16-bit unsigned integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public ushort ToUInt16(IFormatProvider provider) { return Convert.ToUInt16(this.value, provider); } /// /// Converts the value of this instance to an equivalent 32-bit signed integer using the specified culture-specific formatting information. /// /// /// An 32-bit signed integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public int ToInt32(IFormatProvider provider) { return Convert.ToInt32(this.value, provider); } /// /// Converts the value of this instance to an equivalent 32-bit unsigned integer using the specified culture-specific formatting information. /// /// /// An 32-bit unsigned integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public uint ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(this.value, provider); } /// /// Converts the value of this instance to an equivalent 64-bit signed integer using the specified culture-specific formatting information. /// /// /// An 64-bit signed integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public long ToInt64(IFormatProvider provider) { return Convert.ToInt64(this.value, provider); } /// /// Converts the value of this instance to an equivalent 64-bit unsigned integer using the specified culture-specific formatting information. /// /// /// An 64-bit unsigned integer equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public ulong ToUInt64(IFormatProvider provider) { return Convert.ToUInt64(this.value, provider); } /// /// Converts the value of this instance to an equivalent single-precision floating-point number using the specified culture-specific formatting information. /// /// /// A single-precision floating-point number equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public float ToSingle(IFormatProvider provider) { return Convert.ToSingle(this.value, provider); } /// /// Converts the value of this instance to an equivalent double-precision floating-point number using the specified culture-specific formatting information. /// /// /// A double-precision floating-point number equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public double ToDouble(IFormatProvider provider) { return Convert.ToDouble(this.value, provider); } /// /// Converts the value of this instance to an equivalent number using the specified culture-specific formatting information. /// /// /// A number equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public decimal ToDecimal(IFormatProvider provider) { return Convert.ToDecimal(this.value, provider); } /// /// Converts the value of this instance to an equivalent using the specified culture-specific formatting information. /// /// /// A instance equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public DateTime ToDateTime(IFormatProvider provider) { return Convert.ToDateTime(this.value, provider); } /// /// Converts the value of this instance to an equivalent using the specified culture-specific formatting information. /// /// /// A instance equivalent to the value of this instance. /// /// An interface implementation that supplies culture-specific formatting information. 2 public string ToString(IFormatProvider provider) { return Convert.ToString(this.value, provider); } /// /// Converts the value of this instance to an of the specified that has an equivalent value, using the specified culture-specific formatting information. /// /// /// An instance of type whose value is equivalent to the value of this instance. /// /// The to which the value of this instance is converted. An interface implementation that supplies culture-specific formatting information. 2 public object ToType(Type conversionType, IFormatProvider provider) { return Convert.ChangeType(this.value, conversionType, provider); } #endregion } } ================================================ FILE: src/Nancy/ErrorHandling/DefaultStatusCodeHandler.cs ================================================ namespace Nancy.ErrorHandling { using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Nancy.Configuration; using Nancy.Extensions; using Nancy.IO; using Nancy.Responses.Negotiation; using Nancy.ViewEngines; /// /// Default error handler /// public class DefaultStatusCodeHandler : IStatusCodeHandler { private const string DisplayErrorTracesFalseMessage = "Error details are currently disabled.
To enable it, please set TraceConfiguration.DisplayErrorTraces to true.
For example by overriding your Bootstrapper's Configure method and calling
environment.Tracing(enabled: false, displayErrorTraces: true);."; private readonly IDictionary errorMessages; private readonly IDictionary errorPages; private readonly IResponseNegotiator responseNegotiator; private readonly HttpStatusCode[] supportedStatusCodes = { HttpStatusCode.NotFound, HttpStatusCode.InternalServerError }; private readonly TraceConfiguration configuration; /// /// Initializes a new instance of the type. /// /// The response negotiator. /// An instance. public DefaultStatusCodeHandler(IResponseNegotiator responseNegotiator, INancyEnvironment environment) { this.errorMessages = new Dictionary { { HttpStatusCode.NotFound, "The resource you have requested cannot be found." }, { HttpStatusCode.InternalServerError, "Something went horribly, horribly wrong while servicing your request." } }; this.errorPages = new Dictionary { { HttpStatusCode.NotFound, LoadResource("404.html") }, { HttpStatusCode.InternalServerError, LoadResource("500.html") } }; this.responseNegotiator = responseNegotiator; this.configuration = environment.GetValue(); } /// /// Whether the status code is handled /// /// Status code /// The instance of the current request. /// True if handled, false otherwise public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context) { return this.supportedStatusCodes.Any(s => s == statusCode); } /// /// Handle the error code /// /// Status code /// The instance of the current request. /// Nancy Response public void Handle(HttpStatusCode statusCode, NancyContext context) { if (context.Response != null && context.Response.Contents != null && !ReferenceEquals(context.Response.Contents, Response.NoBody)) { return; } if (!this.errorMessages.ContainsKey(statusCode) || !this.errorPages.ContainsKey(statusCode)) { return; } Response existingResponse = null; if (context.Response != null) { existingResponse = context.Response; } // Reset negotiation context to avoid any downstream cast exceptions // from swapping a view model with a `DefaultStatusCodeHandlerResult` context.NegotiationContext = new NegotiationContext(); var details = !this.configuration.DisplayErrorTraces ? DisplayErrorTracesFalseMessage : string.Concat("
", context.GetExceptionDetails().Replace("<", ">").Replace(">", "<"), "
"); var result = new DefaultStatusCodeHandlerResult { Details = details, Message = this.errorMessages[statusCode], StatusCode = statusCode }; try { context.Response = this.responseNegotiator.NegotiateResponse(result, context); context.Response.StatusCode = statusCode; if (existingResponse != null) { context.Response.ReasonPhrase = existingResponse.ReasonPhrase; } return; } catch (ViewNotFoundException) { // No view will be found for `DefaultStatusCodeHandlerResult` // because it is rendered from embedded resources below } this.ModifyResponse(statusCode, context, result); } private void ModifyResponse(HttpStatusCode statusCode, NancyContext context, DefaultStatusCodeHandlerResult result) { if (context.Response == null) { context.Response = new Response { StatusCode = statusCode }; } var contents = this.errorPages[statusCode]; if (!string.IsNullOrEmpty(contents)) { contents = contents.Replace("[DETAILS]", result.Details); } context.Response.ContentType = "text/html"; context.Response.Contents = s => { using (var writer = new StreamWriter(new UnclosableStreamWrapper(s), Encoding.UTF8)) { writer.Write(contents); } }; } private static string LoadResource(string filename) { var resourceStream = typeof(INancyEngine).GetTypeInfo().Assembly.GetManifestResourceStream(string.Format("Nancy.ErrorHandling.Resources.{0}", filename)); if (resourceStream == null) { return string.Empty; } using (var reader = new StreamReader(resourceStream)) { return reader.ReadToEnd(); } } public class DefaultStatusCodeHandlerResult { public HttpStatusCode StatusCode { get; set; } public string Message { get; set; } public string Details { get; set; } } } } ================================================ FILE: src/Nancy/ErrorHandling/Resources/404.html ================================================ Nancy - 404 Not Found
Error Image

404 - Not Found

The resource you have requested cannot be found.

We're sorry ☹

================================================ FILE: src/Nancy/ErrorHandling/Resources/500.html ================================================ Nancy - 500 Internal Server Error
Error Image

500 - Internal Server Error

Something went horribly, horribly wrong while servicing your request.

We're sorry ☹

Error Details

[DETAILS]
================================================ FILE: src/Nancy/ErrorHandling/RouteExecutionEarlyExitException.cs ================================================ namespace Nancy.ErrorHandling { using System; /// /// Here Be Dragons - Using an exception for flow control to hotwire route execution. /// It can be useful to call a method inside a route definition and have that method /// immediately return a response (such as for authentication). This exception is used /// to allow that flow. /// public class RouteExecutionEarlyExitException : Exception { /// /// Initializes a new instance of the class. /// /// /// The response. /// /// /// The reason for the early exit. /// public RouteExecutionEarlyExitException(Response response, string reason = null) { this.Response = response; this.Reason = reason ?? "(none)"; } /// /// Gets or sets the reason for the early exit /// public string Reason { get; set; } /// /// Gets or sets the response /// public Response Response { get; protected set; } } } ================================================ FILE: src/Nancy/ErrorPipeline.cs ================================================ namespace Nancy { using System; /// /// A simple pipeline for on-error hooks. /// Hooks will be executed until either a hook returns a response, or every /// hook has been executed. /// Can be implictly cast to/from the on-error hook delegate signature /// (Func NancyContext, Exception, Response) for assigning to NancyEngine or for building /// composite pipelines. /// /// /// Nancy.NamedPipelineBase{System.Func{Nancy.NancyContext, System.Exception, dynamic}} /// public class ErrorPipeline : NamedPipelineBase> { /// /// Initializes a new instance of the class. /// public ErrorPipeline() { } /// /// Initializes a new instance of the class, with /// the provided . /// /// The number of pipeline delegates. public ErrorPipeline(int capacity) : base(capacity) { } /// /// Performs an implicit conversion from to . /// /// The pipeline. /// /// The result of the conversion. /// public static implicit operator Func(ErrorPipeline pipeline) { return pipeline.Invoke; } /// /// Performs an implicit conversion from to . /// /// The function. /// /// The result of the conversion. /// public static implicit operator ErrorPipeline(Func func) { var pipeline = new ErrorPipeline(); pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends a new func to the ErrorPipeline. /// /// The pipeline. /// The function. /// /// The result of the operator. /// public static ErrorPipeline operator +(ErrorPipeline pipeline, Func func) { pipeline.AddItemToEndOfPipeline(func); return pipeline; } /// /// Appends the items of an ErrorPipeline to the other. /// /// The pipeline to add to. /// The pipeline to add. /// /// The result of the operator. /// public static ErrorPipeline operator +(ErrorPipeline pipelineToAddTo, ErrorPipeline pipelineToAdd) { foreach (var pipelineItem in pipelineToAdd.PipelineItems) { pipelineToAddTo.AddItemToEndOfPipeline(pipelineItem); } return pipelineToAddTo; } /// /// Invoke the pipeline. Each item will be invoked in turn until either an /// item returns a Response, or all items have been invoked. /// /// /// The current context to pass to the items. /// /// /// The exception currently being handled by the error pipeline /// /// /// Response from an item invocation, or null if no response was generated. /// public dynamic Invoke(NancyContext context, Exception ex) { dynamic returnValue = null; using (var enumerator = this.PipelineDelegates.GetEnumerator()) { while (returnValue == null && enumerator.MoveNext()) { returnValue = enumerator.Current.Invoke(context, ex); } } return returnValue; } } } ================================================ FILE: src/Nancy/Extensions/AssemblyExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.IO; using System.Reflection; /// /// Assembly extension methods /// public static class AssemblyExtensions { /// /// Gets exported types from an assembly and catches common errors /// that occur when running under test runners. /// /// Assembly to retrieve from /// An array of types public static Type[] SafeGetExportedTypes(this Assembly assembly) { Type[] types; try { types = assembly.GetExportedTypes(); } catch (FileNotFoundException) { types = ArrayCache.Empty(); } catch (NotSupportedException) { types = ArrayCache.Empty(); } catch (FileLoadException) { // probably assembly version conflict types = ArrayCache.Empty(); } return types; } #if !CORE /// /// Indicates if a given assembly references another which is identified by its name. /// /// The assembly which will be probed. /// The reference assembly name. /// A boolean value indicating if there is a reference. public static bool IsReferencing(this Assembly assembly, AssemblyName referenceName) { if (AssemblyName.ReferenceMatchesDefinition(assembly.GetName(), referenceName)) { return true; } foreach (var referencedAssemblyName in assembly.GetReferencedAssemblies()) { if (AssemblyName.ReferenceMatchesDefinition(referencedAssemblyName, referenceName)) { return true; } } return false; } #endif } } ================================================ FILE: src/Nancy/Extensions/CollectionExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; /// /// Containing extensions for the collection objects. /// public static class CollectionExtensions { /// /// Converts a to a instance. /// /// The to convert. /// An instance. public static IDictionary> ToDictionary(this NameValueCollection source) { return source.AllKeys.ToDictionary>(key => key, source.GetValues); } /// /// Converts an instance to a instance. /// /// The instance to convert. /// A instance. public static NameValueCollection ToNameValueCollection(this IDictionary> source) { var collection = new NameValueCollection(); foreach (var key in source.Keys) { foreach (var value in source[key]) { collection.Add(key, value); } } return collection; } /// /// Merges a collection of instances into a single one. /// /// The list of instances to merge. /// An instance containing the keys and values from the other instances. public static IDictionary Merge(this IEnumerable> dictionaries) { var output = new Dictionary(StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase); foreach (var dictionary in dictionaries.Where(d => d != null)) { foreach (var kvp in dictionary) { if (!output.ContainsKey(kvp.Key)) { output.Add(kvp.Key, kvp.Value); } } } return output; } /// /// Filters a collection based on a provided key selector. /// /// The collection filter. /// The predicate to filter by. /// The type of the collection to filter. /// The type of the key to filter by. /// A instance with the filtered values. public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { var knownKeys = new HashSet(); foreach (TSource element in source) { if (knownKeys.Add(keySelector(element))) { yield return element; } } } } } ================================================ FILE: src/Nancy/Extensions/ContextExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.Text; using Nancy.Responses; /// /// Containing extensions for the object /// public static class ContextExtensions { /// /// Ascertains if a request originated from an Ajax request or not. /// /// The current nancy context /// True if the request was done using ajax, false otherwise public static bool IsAjaxRequest(this NancyContext context) { return context.Request != null && context.Request.IsAjaxRequest(); } /// /// Expands a path to take into account a base path (if any) /// /// Nancy context /// Path to expand /// Expanded path public static string ToFullPath(this NancyContext context, string path) { if (string.IsNullOrEmpty(path)) { return path; } if (context.Request == null) { return path.TrimStart('~'); } if (string.IsNullOrEmpty(context.Request.Url.BasePath)) { return path.TrimStart('~'); } if (!path.StartsWith("~/")) { return path; } return string.Format("{0}{1}", context.Request.Url.BasePath, path.TrimStart('~')); } /// /// Returns a redirect response with the redirect path expanded to take into /// account a base path (if any) /// /// Nancy context /// Path to redirect to /// Redirect response public static RedirectResponse GetRedirect(this NancyContext context, string path) { return new RedirectResponse(context.ToFullPath(path)); } /// /// Retrieves exception details from the context, if any exist /// /// Nancy context /// Exception details public static string GetExceptionDetails(this NancyContext context) { object errorObject; context.Items.TryGetValue(NancyEngine.ERROR_KEY, out errorObject); return (errorObject as string) ?? string.Empty; } /// /// Get a thrown exception from the context. /// /// The context. /// The thrown exception or null if not exception has been thrown. public static Exception GetException(this NancyContext context) { return GetException(context); } /// /// Get a thrown exception of the given type from the context. /// /// The type of exception to get. /// The context. /// The thrown exception or null if not exception has been thrown. public static T GetException(this NancyContext context) where T : Exception { T exception; return TryGetException(context, out exception) ? exception : null; } /// /// Tries to get a thrown exception from the context. /// /// The context. /// The thrown exception. /// true if an exception has been thrown during the request, false otherwise. public static bool TryGetException(this NancyContext context, out Exception exception) { return TryGetException(context, out exception); } /// /// Tries to get a thrown exception of the given type from the context. /// /// The type of exception to get. /// The context. /// The thrown exception. /// true if an exception of the given type has been thrown during the request, false otherwise. public static bool TryGetException(this NancyContext context, out T exception) where T : Exception { object exceptionObject; if (context.Items.TryGetValue(NancyEngine.ERROR_EXCEPTION, out exceptionObject) && exceptionObject is T) { exception = exceptionObject as T; return true; } exception = null; return false; } /// /// Shortcut extension method for writing trace information /// /// Nancy context /// Log delegate public static void WriteTraceLog(this NancyContext context, Action logDelegate) { context.Trace.TraceLog.WriteLog(logDelegate); } /// /// Returns a boolean indicating whether a given url string is local or not /// /// Nancy context /// Url string (relative or absolute) /// True if local, false otherwise public static bool IsLocalUrl(this NancyContext context, string url) { if (string.IsNullOrEmpty(url)) { return false; } if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) { return false; } Uri uri; if (Uri.TryCreate(url, UriKind.Absolute, out uri)) { var currentHostName = context.Request.Url.HostName; // Mono does not populate the uri.Host correctly when url // is in //hostname format causing the simple check to fail. if (uri.Scheme.Equals("file")) { var localFormat = string.Format("//{0}", currentHostName); return !url.StartsWith("//") || url.StartsWith(localFormat); } return uri.Host == currentHostName; } return Uri.TryCreate(url, UriKind.Relative, out uri); } } } ================================================ FILE: src/Nancy/Extensions/MemoryStreamExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.IO; /// /// Extensions for /// public static class MemoryStreamExtensions { /// /// Gets the buffer segment. /// /// The memory stream. /// Buffer segment as bytes public static ArraySegment GetBufferSegment(this MemoryStream stream) { #if NETSTANDARD2_0 ArraySegment buffer; if (stream.TryGetBuffer(out buffer)) { return buffer; } #endif var bytes = stream.GetBytes(); return new ArraySegment(bytes, 0, bytes.Length); } private static byte[] GetBytes(this MemoryStream stream) { #if NETSTANDARD2_0 return stream.ToArray(); // This is all we have if TryGetBuffer fails in GetBufferSegment #else return stream.GetBuffer(); #endif } } } ================================================ FILE: src/Nancy/Extensions/ModelValidationErrorExtensions.cs ================================================ namespace Nancy { using System.Collections.Generic; using Nancy.Validation; /// /// Containing extensions for the property. /// public static class ModelValidationResultExtensions { /// /// Adds a new to the validation results. /// /// A reference to the property. /// The name of the property. /// The validation error message. /// A reference to the property. public static IDictionary> Add(this IDictionary> errors, string name, string errorMessage) { IList value; if (!errors.TryGetValue(name, out value)) { value = new List(); errors[name] = value; } value.Add(new ModelValidationError(name, errorMessage)); return errors; } } } ================================================ FILE: src/Nancy/Extensions/ModuleExtensions.cs ================================================ namespace Nancy.Extensions { using System; using Nancy.ErrorHandling; /// /// Containing extensions for implementations. /// public static class ModuleExtensions { /// /// Extracts the friendly name of a Nancy module given its type. /// /// The module instance /// A string containing the name of the parameter. public static string GetModuleName(this INancyModule module) { var typeName = module.GetType().Name; var offset = typeName.LastIndexOf("Module", StringComparison.Ordinal); if (offset <= 0) { return typeName; } return typeName.Substring(0, offset); } /// /// Returns a boolean indicating whether the route is executing, or whether the module is /// being constructed. /// /// The module instance /// True if the route is being executed, false if the module is being constructed public static bool RouteExecuting(this INancyModule module) { return module.Context != null; } /// /// Adds the before delegate to the Before pipeline if the module is not currently executing, /// or executes the delegate directly and returns any response returned if it is. /// Uses /// /// Current module /// Delegate to add or execute /// Optional reason for the early exit (if necessary) public static void AddBeforeHookOrExecute(this INancyModule module, Func beforeDelegate, string earlyExitReason = null) { if (module.RouteExecuting()) { var result = beforeDelegate.Invoke(module.Context); if (result != null) { throw new RouteExecutionEarlyExitException(result, earlyExitReason); } } else { module.Before.AddItemToEndOfPipeline(beforeDelegate); } } } } ================================================ FILE: src/Nancy/Extensions/ObjectExtensions.cs ================================================ namespace Nancy.Extensions { using System.Collections.Generic; using System.Dynamic; using System.Reflection; /// /// Contains extensions to class. /// public static class ObjectExtensions { /// /// Convert an object to a dynamic type /// /// An object to convert to dynamic /// Returns a dynamic version of the specified type public static dynamic ToDynamic(this object value) { var expando = new ExpandoObject() as IDictionary; foreach (var property in value.GetType().GetTypeInfo().DeclaredProperties) { expando.Add(property.Name, property.GetValue(value)); } return (ExpandoObject)expando; } } } ================================================ FILE: src/Nancy/Extensions/RequestExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.Linq; /// /// Containing extensions for the object /// public static class RequestExtensions { /// /// An extension method making it easy to check if the request was done using ajax /// /// The request made by client /// if the request was done using ajax, otherwise . public static bool IsAjaxRequest(this Request request) { const string ajaxRequestHeaderKey = "X-Requested-With"; const string ajaxRequestHeaderValue = "XMLHttpRequest"; return request.Headers[ajaxRequestHeaderKey].Contains(ajaxRequestHeaderValue); } /// /// Gets a value indicating whether the request is local. /// /// The request made by client /// if the request is local, otherwise . public static bool IsLocal(this Request request) { var userHostAddress = request.UserHostAddress; if (!string.IsNullOrEmpty(userHostAddress)) { if (userHostAddress.Equals("127.0.0.1")) { return true; } if (userHostAddress.Equals("::1")) { return true; } } var url = request.Url; if (string.IsNullOrEmpty(url)) { return false; } Uri uri; if (Uri.TryCreate(url, UriKind.Absolute, out uri)) { return uri.IsLoopback; } // Invalid or relative Request.Url string return false; } } } ================================================ FILE: src/Nancy/Extensions/StreamExtensions.cs ================================================ namespace Nancy.Extensions { using System.IO; using System.Text; /// /// Extensions for Stream. /// public static class StreamExtensions { internal const int BufferSize = 4096; /// /// Gets the request body as a string. /// /// The request body stream. /// The encoding to use, by default. /// The request body as a . public static string AsString(this Stream stream, Encoding encoding = null) { using (var reader = new StreamReader(stream, encoding ?? Encoding.UTF8, true, BufferSize, true)) { if (stream.CanSeek) { var initialPosition = stream.Position; stream.Position = 0; var content = reader.ReadToEnd(); stream.Position = initialPosition; return content; } return string.Empty; } } } } ================================================ FILE: src/Nancy/Extensions/StringExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using Nancy.Helpers; using Nancy.Routing; /// /// Containing extensions for the object. /// public static class StringExtensions { /// /// A regular expression used to manipulate parameterized route segments. /// /// A object. [DebuggerBrowsable(DebuggerBrowsableState.Never)] private static readonly Regex ParameterExpression = new Regex(@"{(?[A-Za-z0-9_]*)(?:\?(?[A-Za-z0-9_-]*))?}", RegexOptions.Compiled); /// /// Extracts information about the parameters in the . /// /// The segment that the information should be extracted from. /// An , containing instances for the parameters in the segment. public static IEnumerable GetParameterDetails(this string segment) { var matches = ParameterExpression .Matches(segment); var nameMatch = matches .Cast() .ToList(); return nameMatch.Select(x => new ParameterSegmentInformation(x.Groups["name"].Value, x.Groups["default"].Value, x.Groups["default"].Success)); } /// /// Checks if a segment contains any parameters. /// /// The segment to check for parameters. /// true if the segment contains a parameter; otherwise false. /// A parameter is defined as a string which is surrounded by a pair of curly brackets. /// The provided value for the segment parameter was null or empty. public static bool IsParameterized(this string segment) { var parameterMatch = ParameterExpression.Match(segment); return parameterMatch.Success; } /// /// Gets a dynamic dictionary back from a Uri query string /// /// The query string to extract values from /// A dynamic dictionary containing the query string values public static DynamicDictionary AsQueryDictionary(this string queryString) { var coll = HttpUtility.ParseQueryString(queryString); var ret = new DynamicDictionary(GlobalizationConfiguration.Default); var found = 0; foreach (var key in coll.AllKeys.Where(key => key != null)) { ret[key] = coll[key]; found++; if (found >= StaticConfiguration.RequestQueryFormMultipartLimit) { break; } } return ret; } /// /// Converts the value from PascalCase to camelCase. /// /// The value. /// System.String. public static string ToCamelCase(this string value) { return value.ConvertFirstCharacter(x => x.ToLowerInvariant()); } /// /// Converts the value from camelCase to PascalCase. /// /// The value. /// System.String. public static string ToPascalCase(this string value) { return value.ConvertFirstCharacter(x => x.ToUpperInvariant()); } private static string ConvertFirstCharacter(this string value, Func converter) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return string.Concat(converter(value.Substring(0, 1)), value.Substring(1)); } } } ================================================ FILE: src/Nancy/Extensions/TypeExtensions.cs ================================================ namespace Nancy.Extensions { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; /// /// Containing extensions for the object. /// public static class TypeExtensions { /// /// Creates an instance of and cast it to . /// /// The type to create an instance of. /// if a non-public constructor can be used, otherwise . public static T CreateInstance(this Type type, bool nonPublic = false) { if (!typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { throw new InvalidOperationException("Unable to create instance of " + type.GetTypeInfo().FullName + "since it can't be cast to " + typeof(T).GetTypeInfo().FullName); } return (T)CreateInstance(type, nonPublic); } /// /// Creates an instance of . /// /// The type to create an instance of. /// if a non-public constructor can be used, otherwise . public static object CreateInstance(this Type type, bool nonPublic = false) { return CreateInstanceInternal(type, nonPublic); } /// /// returns the assembly that the type belongs to /// /// /// The assembly that contains the type public static Assembly GetAssembly(this Type source) { return source.GetTypeInfo().Assembly; } /// /// Checks if a type is an array or not /// /// The type to check. /// if the type is an array, otherwise . public static bool IsArray(this Type source) { return source.GetTypeInfo().BaseType == typeof(Array); } /// /// Determines whether the is assignable from /// taking into account generic definitions /// /// /// Borrowed from: http://tmont.com/blargh/2011/3/determining-if-an-open-generic-type-isassignablefrom-a-type /// public static bool IsAssignableToGenericType(this Type givenType, Type genericType) { if (givenType == null || genericType == null) { return false; } return givenType == genericType || givenType.MapsToGenericTypeDefinition(genericType) || givenType.HasInterfaceThatMapsToGenericTypeDefinition(genericType) || givenType.GetTypeInfo().BaseType.IsAssignableToGenericType(genericType); } /// /// Checks if a type is an collection or not /// /// The type to check. /// if the type is an collection, otherwise . public static bool IsCollection(this Type source) { var collectionType = typeof(ICollection<>); return source.GetTypeInfo().IsGenericType && source .GetInterfaces() .Any(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == collectionType); } /// /// Checks if a type is enumerable or not /// /// The type to check. /// if the type is an enumerable, otherwise . public static bool IsEnumerable(this Type source) { var enumerableType = typeof(IEnumerable<>); return source.GetTypeInfo().IsGenericType && source.GetGenericTypeDefinition() == enumerableType; } /// /// Determines if a type is numeric. Nullable numeric types are considered numeric. /// /// /// Boolean is not considered numeric. /// public static bool IsNumeric(this Type source) { if (source == null) { return false; } var underlyingType = Nullable.GetUnderlyingType(source) ?? source; if (underlyingType.GetTypeInfo().IsEnum) { return false; } switch (underlyingType.GetTypeCode()) { case TypeCode.Byte: case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.SByte: case TypeCode.Single: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return true; default: return false; } } /// /// Filters our all types not assignable to . /// /// The type that all resulting should be assignable to. /// An of instances that should be filtered. /// An of instances. public static IEnumerable NotOfType(this IEnumerable types) { return types.Where(t => !typeof(TType).IsAssignableFrom(t)); } private static bool HasInterfaceThatMapsToGenericTypeDefinition(this Type givenType, Type genericType) { return givenType .GetInterfaces() .Where(it => it.GetTypeInfo().IsGenericType) .Any(it => it.GetGenericTypeDefinition() == genericType); } private static bool MapsToGenericTypeDefinition(this Type givenType, Type genericType) { return genericType.GetTypeInfo().IsGenericTypeDefinition && givenType.GetTypeInfo().IsGenericType && givenType.GetGenericTypeDefinition() == genericType; } /// /// Gets the enum for type code. /// /// The type. /// An enum value representing the type code. public static TypeCode GetTypeCode(this Type type) { if (type == typeof(bool)) return TypeCode.Boolean; else if (type == typeof(char)) return TypeCode.Char; else if (type == typeof(sbyte)) return TypeCode.SByte; else if (type == typeof(byte)) return TypeCode.Byte; else if (type == typeof(short)) return TypeCode.Int16; else if (type == typeof(ushort)) return TypeCode.UInt16; else if (type == typeof(int)) return TypeCode.Int32; else if (type == typeof(uint)) return TypeCode.UInt32; else if (type == typeof(long)) return TypeCode.Int64; else if (type == typeof(ulong)) return TypeCode.UInt64; else if (type == typeof(float)) return TypeCode.Single; else if (type == typeof(double)) return TypeCode.Double; else if (type == typeof(decimal)) return TypeCode.Decimal; else if (type == typeof(DateTime)) return TypeCode.DateTime; else if (type == typeof(string)) return TypeCode.String; else if (type.GetTypeInfo().IsEnum) return GetTypeCode(Enum.GetUnderlyingType(type)); else return TypeCode.Object; } private static object CreateInstanceInternal(Type type, bool nonPublic = false) { #if !NETSTANDARD1_5 return Activator.CreateInstance(type, nonPublic); } #else var constructor = type.GetDefaultConstructor(nonPublic); if (constructor == null) { throw new MissingMethodException("No parameterless constructor defined for this object."); } return constructor.Invoke(Array.Empty()); } private static ConstructorInfo GetDefaultConstructor(this Type type, bool nonPublic = false) { var typeInfo = type.GetTypeInfo(); var constructors = typeInfo.DeclaredConstructors; foreach (var constructor in constructors) { var parameters = constructor.GetParameters(); if (parameters.Length > 0) { continue; } if (!constructor.IsPrivate || nonPublic) { return constructor; } } return null; } #endif } } ================================================ FILE: src/Nancy/FormatterExtensions.cs ================================================ namespace Nancy { using System; using System.IO; using System.Text; using Extensions; using Responses; /// /// Various extensions to return different responses form a . /// public static class FormatterExtensions { private static ISerializer jsonSerializer; private static ISerializer xmlSerializer; /// /// Sends the file at to the /// agent, using for the Content-Type header. /// /// The formatter. /// The application relative file path. /// Value for the Content-Type header. public static Response AsFile(this IResponseFormatter formatter, string applicationRelativeFilePath, string contentType) { return new GenericFileResponse(applicationRelativeFilePath, contentType, formatter.Context); } /// /// Sends the file at to the /// agent, using the file extension and /// to determine the Content-Type header. /// /// The formatter. /// The application relative file path. public static Response AsFile(this IResponseFormatter formatter, string applicationRelativeFilePath) { return new GenericFileResponse(applicationRelativeFilePath, formatter.Context); } /// /// Returns the string to the /// agent, using and /// for the Content-Type header. /// /// The formatter. /// The contents of the response. /// Value for the Content-Type header. /// The encoding to use. public static Response AsText(this IResponseFormatter formatter, string contents, string contentType, Encoding encoding) { return new TextResponse(contents, contentType, encoding); } /// /// Returns the string to the /// agent, using text/plain and /// for the Content-Type header. /// /// The formatter. /// The contents of the response. /// The encoding to use. public static Response AsText(this IResponseFormatter formatter, string contents, Encoding encoding) { return new TextResponse(contents, encoding: encoding); } /// /// Returns the string to the /// agent, using for the Content-Type header. /// /// The formatter. /// The contents of the response. /// Value for the Content-Type header. public static Response AsText(this IResponseFormatter formatter, string contents, string contentType) { return new TextResponse(contents, contentType); } /// /// Returns the string as a text/plain response to the agent. /// /// The formatter. /// The contents of the response. public static Response AsText(this IResponseFormatter formatter, string contents) { return new TextResponse(contents); } /// /// Serializes the to JSON and returns it to the /// agent, optionally using the . /// /// The type of the model. /// The formatter. /// The model to serialize. /// The HTTP status code. Defaults to . public static Response AsJson(this IResponseFormatter formatter, TModel model, HttpStatusCode statusCode = HttpStatusCode.OK) { var serializer = jsonSerializer ?? (jsonSerializer = formatter.SerializerFactory.GetSerializer("application/json")); return new JsonResponse(model, serializer, formatter.Environment) { StatusCode = statusCode }; } /// /// Returns a redirect response to the agent. /// /// The formatter. /// The location to redirect to. /// The redirect type. See . public static Response AsRedirect(this IResponseFormatter formatter, string location, RedirectResponse.RedirectType type = RedirectResponse.RedirectType.SeeOther) { return new RedirectResponse(formatter.Context.ToFullPath(location), type); } /// /// Serializes the to XML and returns it to the /// agent, optionally using the . /// /// The type of the model. /// The formatter. /// The model to serialize. /// The HTTP status code. Defaults to . public static Response AsXml(this IResponseFormatter formatter, TModel model, HttpStatusCode statusCode = HttpStatusCode.OK) { var serializer = xmlSerializer ?? (xmlSerializer = formatter.SerializerFactory.GetSerializer("application/xml")); return new XmlResponse(model, serializer, formatter.Environment); } /// /// Writes the data from the given to the /// agent, using for the Content-Type header. /// /// The formatter. /// The stream to copy from. /// Value for the Content-Type header. public static Response FromStream(this IResponseFormatter formatter, Stream stream, string contentType) { return new StreamResponse(() => stream, contentType); } /// /// Invokes the given to write the stream data to the /// agent, using for the Content-Type header. /// /// The formatter. /// A delegate returning a stream to copy from. /// Value for the Content-Type header. public static Response FromStream(this IResponseFormatter formatter, Func streamDelegate, string contentType) { return new StreamResponse(streamDelegate, contentType); } } } ================================================ FILE: src/Nancy/GlobalizationConfiguration.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; /// /// Globalization configuration /// public class GlobalizationConfiguration { /// /// A default instance of the class /// public static readonly GlobalizationConfiguration Default = new GlobalizationConfiguration { SupportedCultureNames = new[] { CultureInfo.CurrentCulture.Name }, DefaultCulture = CultureInfo.CurrentCulture.Name, DateTimeStyles = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal }; private GlobalizationConfiguration() { } /// /// Initializes a new instance of the class /// /// An array of supported cultures /// The default culture of the application /// The that should be used for date parsing. public GlobalizationConfiguration(IEnumerable supportedCultureNames, string defaultCulture = null, DateTimeStyles? dateTimeStyles = null) { if (supportedCultureNames == null) { throw new ConfigurationException("Invalid Globalization configuration. You must support at least one culture"); } supportedCultureNames = supportedCultureNames.Where(cultureName => !string.IsNullOrEmpty(cultureName)).ToArray(); if (!supportedCultureNames.Any()) { throw new ConfigurationException("Invalid Globalization configuration. You must support at least one culture"); } if (string.IsNullOrEmpty(defaultCulture)) { defaultCulture = supportedCultureNames.First(); } if (!supportedCultureNames.Contains(defaultCulture, StringComparer.OrdinalIgnoreCase)) { throw new ConfigurationException("Invalid Globalization configuration. " + defaultCulture + " does not exist in the supported culture names"); } this.DateTimeStyles = dateTimeStyles ?? Default.DateTimeStyles; this.DefaultCulture = defaultCulture; this.SupportedCultureNames = supportedCultureNames; } /// /// The that should be used for date parsing. /// public DateTimeStyles DateTimeStyles { get; private set; } /// /// The default culture for the application /// public string DefaultCulture { get; private set; } /// /// A set of supported cultures /// public IEnumerable SupportedCultureNames { get; private set; } } } ================================================ FILE: src/Nancy/GlobalizationConfigurationExtensions.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.Globalization; using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class GlobalizationConfigurationExtensions { /// /// Configures /// /// An that should be configured. /// Cultures that the application can accept /// Used to set a default culture for the application /// The that should be used for date parsing. /// If defaultCulture not specified the first supported culture is used public static void Globalization(this INancyEnvironment environment, IEnumerable supportedCultureNames, string defaultCulture = null, DateTimeStyles? dateTimeStyles = null) { environment.AddValue(new GlobalizationConfiguration( supportedCultureNames: supportedCultureNames, defaultCulture: defaultCulture, dateTimeStyles: dateTimeStyles)); } } } ================================================ FILE: src/Nancy/HeadResponse.cs ================================================ namespace Nancy { using System; using System.Globalization; using System.IO; using System.Threading.Tasks; /// /// Represents a HEAD only response. /// public class HeadResponse : Response { private const string ContentLength = "Content-Length"; private readonly Response innerResponse; /// /// Initializes a new instance of the class, with /// the provided . /// /// /// The full response to create the head response from. /// public HeadResponse(Response response) { this.innerResponse = response; this.Contents = stream => { this.CheckAndSetContentLength(this.innerResponse); GetStringContents(string.Empty)(stream); }; this.ContentType = response.ContentType; this.Headers = response.Headers; this.StatusCode = response.StatusCode; this.ReasonPhrase = response.ReasonPhrase; } /// /// Executes at the end of the nancy execution pipeline and before control is passed back to the hosting. /// Can be used to pre-render/validate views while still inside the main pipeline/error handling. /// /// Nancy context /// /// Task for completion/erroring /// public override Task PreExecute(NancyContext context) { return this.innerResponse.PreExecute(context); } private void CheckAndSetContentLength(Response response) { if (this.Headers.ContainsKey(ContentLength)) { return; } using (var nullStream = new NullStream()) { response.Contents.Invoke(nullStream); this.Headers[ContentLength] = nullStream.Length.ToString(CultureInfo.InvariantCulture); } } private sealed class NullStream : Stream { private int bytesWritten; public override void Flush() { } #if !NETSTANDARD1_6 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException(); } public override int EndRead(IAsyncResult asyncResult) { throw new NotSupportedException(); } #endif public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override int ReadByte() { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { // We assume we can't seek and can't overwrite, but don't throw just in case. this.bytesWritten += count; } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override bool CanTimeout { get { return false; } } public override bool CanWrite { get { return true; } } public override long Length { get { return this.bytesWritten; } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } } } } ================================================ FILE: src/Nancy/Helpers/CacheHelpers.cs ================================================ namespace Nancy.Helpers { using System; using System.Linq; /// /// Helper class for caching related functions /// public static class CacheHelpers { /// /// Returns whether to return a not modified response, based on the etag and last modified date /// of the resource, and the current nancy context /// /// Current resource etag, or null /// Current resource last modified, or null /// Current nancy context /// True if not modified should be sent, false otherwise public static bool ReturnNotModified(string etag, DateTime? lastModified, NancyContext context) { if (context == null || context.Request == null) { return false; } var requestEtag = context.Request.Headers.IfNoneMatch.FirstOrDefault(); if (requestEtag != null && !string.IsNullOrEmpty(etag)) { return requestEtag.Equals(etag, StringComparison.Ordinal); } var requestDate = context.Request.Headers.IfModifiedSince; if (requestDate.HasValue && lastModified.HasValue && ((int)(lastModified.Value - requestDate.Value).TotalSeconds) <= 0) { return true; } return false; } } } ================================================ FILE: src/Nancy/Helpers/ExceptionExtensions.cs ================================================ namespace Nancy.Helpers { using System; internal static class ExceptionExtensions { internal static Exception FlattenInnerExceptions(this Exception exception) { var aggregateException = exception as AggregateException; if (aggregateException != null) { var flattenedAggregateException = aggregateException.Flatten(); //If we have more than one exception in the AggregateException //we have to send all exceptions back in order not to swallow any exceptions. if (flattenedAggregateException.InnerExceptions.Count > 1) { return flattenedAggregateException; } return flattenedAggregateException.InnerException; } return exception; } } } ================================================ FILE: src/Nancy/Helpers/HttpEncoder.cs ================================================ // // Authors: // Patrik Torstensson (Patrik.Torstensson@labs2.com) // Wictor Wilén (decode/encode functions) (wictor@ibizkit.se) // Tim Coleman (tim@timcoleman.com) // Gonzalo Paniagua Javier (gonzalo@ximian.com) // Marek Habersack // // (C) 2005-2010 Novell, Inc (http://novell.com/) // // // 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. // namespace Nancy.Helpers { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; #if NET_4_0 public #endif class HttpEncoder { static char[] hexChars = "0123456789abcdef".ToCharArray(); static object entitiesLock = new object(); static SortedDictionary entities; #if NET_4_0 static Lazy defaultEncoder; static Lazy currentEncoderLazy; #else static HttpEncoder defaultEncoder; #endif static HttpEncoder currentEncoder; static IDictionary Entities { get { lock (entitiesLock) { if (entities == null) InitEntities(); return entities; } } } public static HttpEncoder Current { get { #if NET_4_0 if (currentEncoder == null) currentEncoder = currentEncoderLazy.Value; #endif return currentEncoder; } #if NET_4_0 set { if (value == null) throw new ArgumentNullException ("value"); currentEncoder = value; } #endif } public static HttpEncoder Default { get { #if NET_4_0 return defaultEncoder.Value; #else return defaultEncoder; #endif } } static HttpEncoder() { #if NET_4_0 defaultEncoder = new Lazy (() => new HttpEncoder ()); currentEncoderLazy = new Lazy (new Func (GetCustomEncoderFromConfig)); #else defaultEncoder = new HttpEncoder(); currentEncoder = defaultEncoder; #endif } public HttpEncoder() { } #if NET_4_0 protected internal virtual #else internal static #endif void HeaderNameValueEncode(string headerName, string headerValue, out string encodedHeaderName, out string encodedHeaderValue) { if (string.IsNullOrEmpty(headerName)) encodedHeaderName = headerName; else encodedHeaderName = EncodeHeaderString(headerName); if (string.IsNullOrEmpty(headerValue)) encodedHeaderValue = headerValue; else encodedHeaderValue = EncodeHeaderString(headerValue); } static void StringBuilderAppend(string s, ref StringBuilder sb) { if (sb == null) sb = new StringBuilder(s); else sb.Append(s); } static string EncodeHeaderString(string input) { StringBuilder sb = null; char ch; for (int i = 0; i < input.Length; i++) { ch = input[i]; if ((ch < 32 && ch != 9) || ch == 127) StringBuilderAppend(String.Format("%{0:x2}", (int)ch), ref sb); } if (sb != null) return sb.ToString(); return input; } #if NET_4_0 protected internal virtual void HtmlAttributeEncode (string value, TextWriter output) { if (output == null) throw new ArgumentNullException ("output"); if (string.IsNullOrEmpty (value)) return; output.Write (HtmlAttributeEncode (value)); } protected internal virtual void HtmlDecode (string value, TextWriter output) { if (output == null) throw new ArgumentNullException ("output"); output.Write (HtmlDecode (value)); } protected internal virtual void HtmlEncode (string value, TextWriter output) { if (output == null) throw new ArgumentNullException ("output"); output.Write (HtmlEncode (value)); } protected internal virtual byte[] UrlEncode (byte[] bytes, int offset, int count) { return UrlEncodeToBytes (bytes, offset, count); } static HttpEncoder GetCustomEncoderFromConfig () { var cfg = HttpRuntime.Section; string typeName = cfg.EncoderType; if (String.Compare (typeName, "System.Web.Util.HttpEncoder", StringComparison.OrdinalIgnoreCase) == 0) return Default; Type t = Type.GetType (typeName, false); if (t == null) throw new ConfigurationErrorsException (String.Format ("Could not load type '{0}'.", typeName)); if (!typeof (HttpEncoder).IsAssignableFrom (t)) throw new ConfigurationErrorsException ( String.Format ("'{0}' is not allowed here because it does not extend class 'System.Web.Util.HttpEncoder'.", typeName) ); return Activator.CreateInstance (t, false) as HttpEncoder; } #endif #if NET_4_0 protected internal virtual #else internal static #endif string UrlPathEncode(string value) { if (string.IsNullOrEmpty(value)) return value; MemoryStream result = new MemoryStream(); int length = value.Length; for (int i = 0; i < length; i++) UrlPathEncodeChar(value[i], result); return Encoding.ASCII.GetString(result.ToArray()); } internal static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) throw new ArgumentNullException("bytes"); int blen = bytes.Length; if (blen == 0) return ArrayCache.Empty(); if (offset < 0 || offset >= blen) throw new ArgumentOutOfRangeException("offset"); if (count < 0 || count > blen - offset) throw new ArgumentOutOfRangeException("count"); MemoryStream result = new MemoryStream(count); int end = offset + count; for (int i = offset; i < end; i++) UrlEncodeChar((char)bytes[i], result, false); return result.ToArray(); } internal static string HtmlEncode(string s) { if (s == null) return null; if (s.Length == 0) return String.Empty; bool needEncode = false; for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159 #if NET_4_0 || c == '\'' #endif ) { needEncode = true; break; } } if (!needEncode) return s; StringBuilder output = new StringBuilder(); char ch; int len = s.Length; for (int i = 0; i < len; i++) { switch (s[i]) { case '&': output.Append("&"); break; case '>': output.Append(">"); break; case '<': output.Append("<"); break; case '"': output.Append("""); break; #if NET_4_0 case '\'': output.Append ("'"); break; #endif case '\uff1c': output.Append("<"); break; case '\uff1e': output.Append(">"); break; default: ch = s[i]; if (ch > 159 && ch < 256) { output.Append("&#"); output.Append(((int)ch).ToString(CultureInfo.InvariantCulture)); output.Append(";"); } else output.Append(ch); break; } } return output.ToString(); } internal static string HtmlAttributeEncode(string s) { #if NET_4_0 if (string.IsNullOrEmpty (s)) return String.Empty; #else if (s == null) return null; if (s.Length == 0) return String.Empty; #endif bool needEncode = false; for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c == '&' || c == '"' || c == '<' #if NET_4_0 || c == '\'' #endif ) { needEncode = true; break; } } if (!needEncode) return s; StringBuilder output = new StringBuilder(); int len = s.Length; for (int i = 0; i < len; i++) switch (s[i]) { case '&': output.Append("&"); break; case '"': output.Append("""); break; case '<': output.Append("<"); break; #if NET_4_0 case '\'': output.Append ("'"); break; #endif default: output.Append(s[i]); break; } return output.ToString(); } internal static string HtmlDecode(string s) { if (s == null) return null; if (s.Length == 0) return String.Empty; if (s.IndexOf('&') == -1) return s; #if NET_4_0 StringBuilder rawEntity = new StringBuilder (); #endif StringBuilder entity = new StringBuilder(); StringBuilder output = new StringBuilder(); int len = s.Length; // 0 -> nothing, // 1 -> right after '&' // 2 -> between '&' and ';' but no '#' // 3 -> '#' found after '&' and getting numbers int state = 0; int number = 0; bool is_hex_value = false; bool have_trailing_digits = false; for (int i = 0; i < len; i++) { char c = s[i]; if (state == 0) { if (c == '&') { entity.Append(c); #if NET_4_0 rawEntity.Append (c); #endif state = 1; } else { output.Append(c); } continue; } if (c == '&') { state = 1; if (have_trailing_digits) { entity.Append(number.ToString(CultureInfo.InvariantCulture)); have_trailing_digits = false; } output.Append(entity.ToString()); entity.Length = 0; entity.Append('&'); continue; } if (state == 1) { if (c == ';') { state = 0; output.Append(entity.ToString()); output.Append(c); entity.Length = 0; } else { number = 0; is_hex_value = false; if (c != '#') { state = 2; } else { state = 3; } entity.Append(c); #if NET_4_0 rawEntity.Append (c); #endif } } else if (state == 2) { entity.Append(c); if (c == ';') { string key = entity.ToString(); char value; if (key.Length > 1 && Entities.TryGetValue(key.Substring(1, key.Length - 2), out value)) key = value.ToString(); output.Append(key); state = 0; entity.Length = 0; #if NET_4_0 rawEntity.Length = 0; #endif } } else if (state == 3) { if (c == ';') { #if NET_4_0 if (number == 0) output.Append (rawEntity.ToString () + ";"); else #endif if (number > 65535) { output.Append("&#"); output.Append(number.ToString(CultureInfo.InvariantCulture)); output.Append(";"); } else { output.Append((char)number); } state = 0; entity.Length = 0; #if NET_4_0 rawEntity.Length = 0; #endif have_trailing_digits = false; } else if (is_hex_value && IsHexDigit(c)) { number = number * 16 + FromHex(c); have_trailing_digits = true; #if NET_4_0 rawEntity.Append (c); #endif } else if (Char.IsDigit(c)) { number = number * 10 + ((int)c - '0'); have_trailing_digits = true; #if NET_4_0 rawEntity.Append (c); #endif } else if (number == 0 && (c == 'x' || c == 'X')) { is_hex_value = true; #if NET_4_0 rawEntity.Append (c); #endif } else { state = 2; if (have_trailing_digits) { entity.Append(number.ToString(CultureInfo.InvariantCulture)); have_trailing_digits = false; } entity.Append(c); } } } if (entity.Length > 0) { output.Append(entity.ToString()); } else if (have_trailing_digits) { output.Append(number.ToString(CultureInfo.InvariantCulture)); } return output.ToString(); } internal static bool IsHexDigit(char character) { //implementation from https://github.com/dotnet/corefx/blob/ac67ffac987d0c27236c4a6cf1255c2bcbc7fe7d/src/System.Private.Uri/src/System/Uri.cs#L1366 return ((character >= '0') && (character <= '9')) || ((character >= 'A') && (character <= 'F')) || ((character >= 'a') && (character <= 'f')); } internal static int FromHex(char digit) { //implementation from https://github.com/dotnet/corefx/blob/ac67ffac987d0c27236c4a6cf1255c2bcbc7fe7d/src/System.Private.Uri/src/System/Uri.cs#L1379 if (((digit >= '0') && (digit <= '9')) || ((digit >= 'A') && (digit <= 'F')) || ((digit >= 'a') && (digit <= 'f'))) { return (digit <= '9') ? ((int)digit - (int)'0') : (((digit <= 'F') ? ((int)digit - (int)'A') : ((int)digit - (int)'a')) + 10); } throw new ArgumentException("digit"); } internal static bool NotEncoded(char c) { return (c == '!' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_' #if !NET_4_0 || c == '\'' #endif ); } internal static void UrlEncodeChar(char c, Stream result, bool isUnicode) { if (c > 255) { //FIXME: what happens when there is an internal error? //if (!isUnicode) // throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256"); int idx; int i = (int)c; result.WriteByte((byte)'%'); result.WriteByte((byte)'u'); idx = i >> 12; result.WriteByte((byte)hexChars[idx]); idx = (i >> 8) & 0x0F; result.WriteByte((byte)hexChars[idx]); idx = (i >> 4) & 0x0F; result.WriteByte((byte)hexChars[idx]); idx = i & 0x0F; result.WriteByte((byte)hexChars[idx]); return; } if (c > ' ' && NotEncoded(c)) { result.WriteByte((byte)c); return; } if (c == ' ') { result.WriteByte((byte)'+'); return; } if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { if (isUnicode && c > 127) { result.WriteByte((byte)'%'); result.WriteByte((byte)'u'); result.WriteByte((byte)'0'); result.WriteByte((byte)'0'); } else result.WriteByte((byte)'%'); int idx = ((int)c) >> 4; result.WriteByte((byte)hexChars[idx]); idx = ((int)c) & 0x0F; result.WriteByte((byte)hexChars[idx]); } else result.WriteByte((byte)c); } internal static void UrlPathEncodeChar(char c, Stream result) { if (c < 33 || c > 126) { byte[] bIn = Encoding.UTF8.GetBytes(c.ToString()); for (int i = 0; i < bIn.Length; i++) { result.WriteByte((byte)'%'); int idx = ((int)bIn[i]) >> 4; result.WriteByte((byte)hexChars[idx]); idx = ((int)bIn[i]) & 0x0F; result.WriteByte((byte)hexChars[idx]); } } else if (c == ' ') { result.WriteByte((byte)'%'); result.WriteByte((byte)'2'); result.WriteByte((byte)'0'); } else result.WriteByte((byte)c); } static void InitEntities() { // Build the hash table of HTML entity references. This list comes // from the HTML 4.01 W3C recommendation. entities = new SortedDictionary(StringComparer.Ordinal); entities.Add("nbsp", '\u00A0'); entities.Add("iexcl", '\u00A1'); entities.Add("cent", '\u00A2'); entities.Add("pound", '\u00A3'); entities.Add("curren", '\u00A4'); entities.Add("yen", '\u00A5'); entities.Add("brvbar", '\u00A6'); entities.Add("sect", '\u00A7'); entities.Add("uml", '\u00A8'); entities.Add("copy", '\u00A9'); entities.Add("ordf", '\u00AA'); entities.Add("laquo", '\u00AB'); entities.Add("not", '\u00AC'); entities.Add("shy", '\u00AD'); entities.Add("reg", '\u00AE'); entities.Add("macr", '\u00AF'); entities.Add("deg", '\u00B0'); entities.Add("plusmn", '\u00B1'); entities.Add("sup2", '\u00B2'); entities.Add("sup3", '\u00B3'); entities.Add("acute", '\u00B4'); entities.Add("micro", '\u00B5'); entities.Add("para", '\u00B6'); entities.Add("middot", '\u00B7'); entities.Add("cedil", '\u00B8'); entities.Add("sup1", '\u00B9'); entities.Add("ordm", '\u00BA'); entities.Add("raquo", '\u00BB'); entities.Add("frac14", '\u00BC'); entities.Add("frac12", '\u00BD'); entities.Add("frac34", '\u00BE'); entities.Add("iquest", '\u00BF'); entities.Add("Agrave", '\u00C0'); entities.Add("Aacute", '\u00C1'); entities.Add("Acirc", '\u00C2'); entities.Add("Atilde", '\u00C3'); entities.Add("Auml", '\u00C4'); entities.Add("Aring", '\u00C5'); entities.Add("AElig", '\u00C6'); entities.Add("Ccedil", '\u00C7'); entities.Add("Egrave", '\u00C8'); entities.Add("Eacute", '\u00C9'); entities.Add("Ecirc", '\u00CA'); entities.Add("Euml", '\u00CB'); entities.Add("Igrave", '\u00CC'); entities.Add("Iacute", '\u00CD'); entities.Add("Icirc", '\u00CE'); entities.Add("Iuml", '\u00CF'); entities.Add("ETH", '\u00D0'); entities.Add("Ntilde", '\u00D1'); entities.Add("Ograve", '\u00D2'); entities.Add("Oacute", '\u00D3'); entities.Add("Ocirc", '\u00D4'); entities.Add("Otilde", '\u00D5'); entities.Add("Ouml", '\u00D6'); entities.Add("times", '\u00D7'); entities.Add("Oslash", '\u00D8'); entities.Add("Ugrave", '\u00D9'); entities.Add("Uacute", '\u00DA'); entities.Add("Ucirc", '\u00DB'); entities.Add("Uuml", '\u00DC'); entities.Add("Yacute", '\u00DD'); entities.Add("THORN", '\u00DE'); entities.Add("szlig", '\u00DF'); entities.Add("agrave", '\u00E0'); entities.Add("aacute", '\u00E1'); entities.Add("acirc", '\u00E2'); entities.Add("atilde", '\u00E3'); entities.Add("auml", '\u00E4'); entities.Add("aring", '\u00E5'); entities.Add("aelig", '\u00E6'); entities.Add("ccedil", '\u00E7'); entities.Add("egrave", '\u00E8'); entities.Add("eacute", '\u00E9'); entities.Add("ecirc", '\u00EA'); entities.Add("euml", '\u00EB'); entities.Add("igrave", '\u00EC'); entities.Add("iacute", '\u00ED'); entities.Add("icirc", '\u00EE'); entities.Add("iuml", '\u00EF'); entities.Add("eth", '\u00F0'); entities.Add("ntilde", '\u00F1'); entities.Add("ograve", '\u00F2'); entities.Add("oacute", '\u00F3'); entities.Add("ocirc", '\u00F4'); entities.Add("otilde", '\u00F5'); entities.Add("ouml", '\u00F6'); entities.Add("divide", '\u00F7'); entities.Add("oslash", '\u00F8'); entities.Add("ugrave", '\u00F9'); entities.Add("uacute", '\u00FA'); entities.Add("ucirc", '\u00FB'); entities.Add("uuml", '\u00FC'); entities.Add("yacute", '\u00FD'); entities.Add("thorn", '\u00FE'); entities.Add("yuml", '\u00FF'); entities.Add("fnof", '\u0192'); entities.Add("Alpha", '\u0391'); entities.Add("Beta", '\u0392'); entities.Add("Gamma", '\u0393'); entities.Add("Delta", '\u0394'); entities.Add("Epsilon", '\u0395'); entities.Add("Zeta", '\u0396'); entities.Add("Eta", '\u0397'); entities.Add("Theta", '\u0398'); entities.Add("Iota", '\u0399'); entities.Add("Kappa", '\u039A'); entities.Add("Lambda", '\u039B'); entities.Add("Mu", '\u039C'); entities.Add("Nu", '\u039D'); entities.Add("Xi", '\u039E'); entities.Add("Omicron", '\u039F'); entities.Add("Pi", '\u03A0'); entities.Add("Rho", '\u03A1'); entities.Add("Sigma", '\u03A3'); entities.Add("Tau", '\u03A4'); entities.Add("Upsilon", '\u03A5'); entities.Add("Phi", '\u03A6'); entities.Add("Chi", '\u03A7'); entities.Add("Psi", '\u03A8'); entities.Add("Omega", '\u03A9'); entities.Add("alpha", '\u03B1'); entities.Add("beta", '\u03B2'); entities.Add("gamma", '\u03B3'); entities.Add("delta", '\u03B4'); entities.Add("epsilon", '\u03B5'); entities.Add("zeta", '\u03B6'); entities.Add("eta", '\u03B7'); entities.Add("theta", '\u03B8'); entities.Add("iota", '\u03B9'); entities.Add("kappa", '\u03BA'); entities.Add("lambda", '\u03BB'); entities.Add("mu", '\u03BC'); entities.Add("nu", '\u03BD'); entities.Add("xi", '\u03BE'); entities.Add("omicron", '\u03BF'); entities.Add("pi", '\u03C0'); entities.Add("rho", '\u03C1'); entities.Add("sigmaf", '\u03C2'); entities.Add("sigma", '\u03C3'); entities.Add("tau", '\u03C4'); entities.Add("upsilon", '\u03C5'); entities.Add("phi", '\u03C6'); entities.Add("chi", '\u03C7'); entities.Add("psi", '\u03C8'); entities.Add("omega", '\u03C9'); entities.Add("thetasym", '\u03D1'); entities.Add("upsih", '\u03D2'); entities.Add("piv", '\u03D6'); entities.Add("bull", '\u2022'); entities.Add("hellip", '\u2026'); entities.Add("prime", '\u2032'); entities.Add("Prime", '\u2033'); entities.Add("oline", '\u203E'); entities.Add("frasl", '\u2044'); entities.Add("weierp", '\u2118'); entities.Add("image", '\u2111'); entities.Add("real", '\u211C'); entities.Add("trade", '\u2122'); entities.Add("alefsym", '\u2135'); entities.Add("larr", '\u2190'); entities.Add("uarr", '\u2191'); entities.Add("rarr", '\u2192'); entities.Add("darr", '\u2193'); entities.Add("harr", '\u2194'); entities.Add("crarr", '\u21B5'); entities.Add("lArr", '\u21D0'); entities.Add("uArr", '\u21D1'); entities.Add("rArr", '\u21D2'); entities.Add("dArr", '\u21D3'); entities.Add("hArr", '\u21D4'); entities.Add("forall", '\u2200'); entities.Add("part", '\u2202'); entities.Add("exist", '\u2203'); entities.Add("empty", '\u2205'); entities.Add("nabla", '\u2207'); entities.Add("isin", '\u2208'); entities.Add("notin", '\u2209'); entities.Add("ni", '\u220B'); entities.Add("prod", '\u220F'); entities.Add("sum", '\u2211'); entities.Add("minus", '\u2212'); entities.Add("lowast", '\u2217'); entities.Add("radic", '\u221A'); entities.Add("prop", '\u221D'); entities.Add("infin", '\u221E'); entities.Add("ang", '\u2220'); entities.Add("and", '\u2227'); entities.Add("or", '\u2228'); entities.Add("cap", '\u2229'); entities.Add("cup", '\u222A'); entities.Add("int", '\u222B'); entities.Add("there4", '\u2234'); entities.Add("sim", '\u223C'); entities.Add("cong", '\u2245'); entities.Add("asymp", '\u2248'); entities.Add("ne", '\u2260'); entities.Add("equiv", '\u2261'); entities.Add("le", '\u2264'); entities.Add("ge", '\u2265'); entities.Add("sub", '\u2282'); entities.Add("sup", '\u2283'); entities.Add("nsub", '\u2284'); entities.Add("sube", '\u2286'); entities.Add("supe", '\u2287'); entities.Add("oplus", '\u2295'); entities.Add("otimes", '\u2297'); entities.Add("perp", '\u22A5'); entities.Add("sdot", '\u22C5'); entities.Add("lceil", '\u2308'); entities.Add("rceil", '\u2309'); entities.Add("lfloor", '\u230A'); entities.Add("rfloor", '\u230B'); entities.Add("lang", '\u2329'); entities.Add("rang", '\u232A'); entities.Add("loz", '\u25CA'); entities.Add("spades", '\u2660'); entities.Add("clubs", '\u2663'); entities.Add("hearts", '\u2665'); entities.Add("diams", '\u2666'); entities.Add("quot", '\u0022'); entities.Add("amp", '\u0026'); entities.Add("lt", '\u003C'); entities.Add("gt", '\u003E'); entities.Add("OElig", '\u0152'); entities.Add("oelig", '\u0153'); entities.Add("Scaron", '\u0160'); entities.Add("scaron", '\u0161'); entities.Add("Yuml", '\u0178'); entities.Add("circ", '\u02C6'); entities.Add("tilde", '\u02DC'); entities.Add("ensp", '\u2002'); entities.Add("emsp", '\u2003'); entities.Add("thinsp", '\u2009'); entities.Add("zwnj", '\u200C'); entities.Add("zwj", '\u200D'); entities.Add("lrm", '\u200E'); entities.Add("rlm", '\u200F'); entities.Add("ndash", '\u2013'); entities.Add("mdash", '\u2014'); entities.Add("lsquo", '\u2018'); entities.Add("rsquo", '\u2019'); entities.Add("sbquo", '\u201A'); entities.Add("ldquo", '\u201C'); entities.Add("rdquo", '\u201D'); entities.Add("bdquo", '\u201E'); entities.Add("dagger", '\u2020'); entities.Add("Dagger", '\u2021'); entities.Add("permil", '\u2030'); entities.Add("lsaquo", '\u2039'); entities.Add("rsaquo", '\u203A'); entities.Add("euro", '\u20AC'); } } } ================================================ FILE: src/Nancy/Helpers/HttpUtility.cs ================================================ #pragma warning disable CS1591, CS1574, CS1711, CS1712 // Disable XML comment related warnings // // System.Web.HttpUtility // // Authors: // Patrik Torstensson (Patrik.Torstensson@labs2.com) // Wictor Wiln (decode/encode functions) (wictor@ibizkit.se) // Tim Coleman (tim@timcoleman.com) // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com) // // 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. // namespace Nancy.Helpers { using Extensions; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Text; public sealed class HttpUtility { sealed class HttpQSCollection : NameValueCollection { public HttpQSCollection() : this(StaticConfiguration.CaseSensitive) { } public HttpQSCollection(bool caseSensitive) : base(caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase) { } public override string ToString() { int count = Count; if (count == 0) return ""; StringBuilder sb = new StringBuilder(); string[] keys = AllKeys; for (int i = 0; i < count; i++) { sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]); } if (sb.Length > 0) sb.Length--; return sb.ToString(); } } #region Constructors public HttpUtility() { } #endregion // Constructors #region Methods public static void HtmlAttributeEncode(string s, TextWriter output) { if (output == null) { #if NET_4_0 throw new ArgumentNullException ("output"); #else throw new NullReferenceException(".NET emulation"); #endif } #if NET_4_0 HttpEncoder.Current.HtmlAttributeEncode (s, output); #else output.Write(HttpEncoder.HtmlAttributeEncode(s)); #endif } public static string HtmlAttributeEncode(string s) { #if NET_4_0 if (s == null) return null; using (var sw = new StringWriter ()) { HttpEncoder.Current.HtmlAttributeEncode (s, sw); return sw.ToString (); } #else return HttpEncoder.HtmlAttributeEncode(s); #endif } public static string UrlDecode(string str) { return UrlDecode(str, Encoding.UTF8); } static char[] GetChars(MemoryStream b, Encoding e) { var buffer = b.GetBufferSegment(); return e.GetChars(buffer.Array, buffer.Offset, buffer.Count); } static void WriteCharBytes(IList buf, char ch, Encoding e) { if (ch > 255) { foreach (byte b in e.GetBytes(new char[] { ch })) buf.Add(b); } else buf.Add((byte)ch); } public static string UrlDecode(string s, Encoding e) { if (null == s) return null; if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1) return s; if (e == null) e = Encoding.UTF8; long len = s.Length; var bytes = new List(); int xchar; char ch; for (int i = 0; i < len; i++) { ch = s[i]; if (ch == '%' && i + 2 < len && s[i + 1] != '%') { if (s[i + 1] == 'u' && i + 5 < len) { // unicode hex sequence xchar = GetChar(s, i + 2, 4); if (xchar != -1) { WriteCharBytes(bytes, (char)xchar, e); i += 5; } else WriteCharBytes(bytes, '%', e); } else if ((xchar = GetChar(s, i + 1, 2)) != -1) { WriteCharBytes(bytes, (char)xchar, e); i += 2; } else { WriteCharBytes(bytes, '%', e); } continue; } if (ch == '+') WriteCharBytes(bytes, ' ', e); else WriteCharBytes(bytes, ch, e); } byte[] buf = bytes.ToArray(); bytes = null; return e.GetString(buf); } public static string UrlDecode(byte[] bytes, Encoding e) { if (bytes == null) return null; return UrlDecode(bytes, 0, bytes.Length, e); } static int GetInt(byte b) { char c = (char)b; if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static int GetChar(byte[] bytes, int offset, int length) { int value = 0; int end = length + offset; for (int i = offset; i < end; i++) { int current = GetInt(bytes[i]); if (current == -1) return -1; value = (value << 4) + current; } return value; } static int GetChar(string str, int offset, int length) { int val = 0; int end = length + offset; for (int i = offset; i < end; i++) { char c = str[i]; if (c > 127) return -1; int current = GetInt((byte)c); if (current == -1) return -1; val = (val << 4) + current; } return val; } public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e) { if (bytes == null) return null; if (count == 0) return string.Empty; if (offset < 0 || offset > bytes.Length) throw new ArgumentOutOfRangeException("offset"); if (count < 0 || offset + count > bytes.Length) throw new ArgumentOutOfRangeException("count"); var output = new StringBuilder(); var acc = new MemoryStream(); int end = count + offset; int xchar; for (int i = offset; i < end; i++) { if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%') { if (bytes[i + 1] == (byte)'u' && i + 5 < end) { if (acc.Length > 0) { output.Append(GetChars(acc, e)); acc.SetLength(0); } xchar = GetChar(bytes, i + 2, 4); if (xchar != -1) { output.Append((char)xchar); i += 5; continue; } } else if ((xchar = GetChar(bytes, i + 1, 2)) != -1) { acc.WriteByte((byte)xchar); i += 2; continue; } } if (acc.Length > 0) { output.Append(GetChars(acc, e)); acc.SetLength(0); } if (bytes[i] == '+') { output.Append(' '); } else { output.Append((char)bytes[i]); } } if (acc.Length > 0) { output.Append(GetChars(acc, e)); } acc = null; return output.ToString(); } public static byte[] UrlDecodeToBytes(byte[] bytes) { if (bytes == null) return null; return UrlDecodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlDecodeToBytes(string str) { return UrlDecodeToBytes(str, Encoding.UTF8); } public static byte[] UrlDecodeToBytes(string str, Encoding e) { if (str == null) return null; if (e == null) throw new ArgumentNullException("e"); return UrlDecodeToBytes(e.GetBytes(str)); } public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) return null; if (count == 0) return ArrayCache.Empty(); int len = bytes.Length; if (offset < 0 || offset >= len) throw new ArgumentOutOfRangeException("offset"); if (count < 0 || offset > len - count) throw new ArgumentOutOfRangeException("count"); MemoryStream result = new MemoryStream(); int end = offset + count; for (int i = offset; i < end; i++) { char c = (char)bytes[i]; if (c == '+') { c = ' '; } else if (c == '%' && i < end - 2) { int xchar = GetChar(bytes, i + 1, 2); if (xchar != -1) { c = (char)xchar; i += 2; } } result.WriteByte((byte)c); } return result.ToArray(); } public static string UrlEncode(string str) { return UrlEncode(str, Encoding.UTF8); } public static string UrlEncode(string s, Encoding Enc) { if (s == null) return null; if (s == String.Empty) return String.Empty; bool needEncode = false; int len = s.Length; for (int i = 0; i < len; i++) { char c = s[i]; if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { if (HttpEncoder.NotEncoded(c)) continue; needEncode = true; break; } } if (!needEncode) return s; // avoided GetByteCount call byte[] bytes = new byte[Enc.GetMaxByteCount(s.Length)]; int realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0); return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, realLen)); } public static string UrlEncode(byte[] bytes) { if (bytes == null) return null; if (bytes.Length == 0) return String.Empty; return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length)); } public static string UrlEncode(byte[] bytes, int offset, int count) { if (bytes == null) return null; if (bytes.Length == 0) return String.Empty; return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count)); } public static byte[] UrlEncodeToBytes(string str) { return UrlEncodeToBytes(str, Encoding.UTF8); } public static byte[] UrlEncodeToBytes(string str, Encoding e) { if (str == null) return null; if (str.Length == 0) return ArrayCache.Empty(); byte[] bytes = e.GetBytes(str); return UrlEncodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlEncodeToBytes(byte[] bytes) { if (bytes == null) return null; if (bytes.Length == 0) return ArrayCache.Empty(); return UrlEncodeToBytes(bytes, 0, bytes.Length); } public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count) { if (bytes == null) return null; #if NET_4_0 return HttpEncoder.Current.UrlEncode (bytes, offset, count); #else return HttpEncoder.UrlEncodeToBytes(bytes, offset, count); #endif } public static string UrlEncodeUnicode(string str) { if (str == null) return null; return Encoding.ASCII.GetString(UrlEncodeUnicodeToBytes(str)); } public static byte[] UrlEncodeUnicodeToBytes(string str) { if (str == null) return null; if (str.Length == 0) return ArrayCache.Empty(); MemoryStream result = new MemoryStream(str.Length); foreach (char c in str) { HttpEncoder.UrlEncodeChar(c, result, true); } return result.ToArray(); } /// /// Decodes an HTML-encoded string and returns the decoded string. /// /// The HTML string to decode. /// The decoded text. public static string HtmlDecode(string s) { #if NET_4_0 if (s == null) return null; using (var sw = new StringWriter ()) { HttpEncoder.Current.HtmlDecode (s, sw); return sw.ToString (); } #else return HttpEncoder.HtmlDecode(s); #endif } /// /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream. /// /// The HTML string to decode /// The TextWriter output stream containing the decoded string. public static void HtmlDecode(string s, TextWriter output) { if (output == null) { #if NET_4_0 throw new ArgumentNullException ("output"); #else throw new NullReferenceException(".NET emulation"); #endif } if (!string.IsNullOrEmpty(s)) { #if NET_4_0 HttpEncoder.Current.HtmlDecode (s, output); #else output.Write(HttpEncoder.HtmlDecode(s)); #endif } } public static string HtmlEncode(string s) { #if NET_4_0 if (s == null) return null; using (var sw = new StringWriter ()) { HttpEncoder.Current.HtmlEncode (s, sw); return sw.ToString (); } #else return HttpEncoder.HtmlEncode(s); #endif } /// /// HTML-encodes a string and sends the resulting output to a TextWriter output stream. /// /// The string to encode. /// The TextWriter output stream containing the encoded string. public static void HtmlEncode(string s, TextWriter output) { if (output == null) { #if NET_4_0 throw new ArgumentNullException ("output"); #else throw new NullReferenceException(".NET emulation"); #endif } if (!string.IsNullOrEmpty(s)) { #if NET_4_0 HttpEncoder.Current.HtmlEncode (s, output); #else output.Write(HttpEncoder.HtmlEncode(s)); #endif } } #if NET_4_0 public static string HtmlEncode (object value) { if (value == null) return null; IHtmlString htmlString = value as IHtmlString; if (htmlString != null) return htmlString.ToHtmlString (); return HtmlEncode (value.ToString ()); } public static string JavaScriptStringEncode (string value) { return JavaScriptStringEncode (value, false); } public static string JavaScriptStringEncode (string value, bool addDoubleQuotes) { if (string.IsNullOrEmpty (value)) return addDoubleQuotes ? "\"\"" : String.Empty; int len = value.Length; bool needEncode = false; char c; for (int i = 0; i < len; i++) { c = value [i]; if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92) { needEncode = true; break; } } if (!needEncode) return addDoubleQuotes ? "\"" + value + "\"" : value; var sb = new StringBuilder (); if (addDoubleQuotes) sb.Append ('"'); for (int i = 0; i < len; i++) { c = value [i]; if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62) sb.AppendFormat ("\\u{0:x4}", (int)c); else switch ((int)c) { case 8: sb.Append ("\\b"); break; case 9: sb.Append ("\\t"); break; case 10: sb.Append ("\\n"); break; case 12: sb.Append ("\\f"); break; case 13: sb.Append ("\\r"); break; case 34: sb.Append ("\\\""); break; case 92: sb.Append ("\\\\"); break; default: sb.Append (c); break; } } if (addDoubleQuotes) sb.Append ('"'); return sb.ToString (); } #endif public static string UrlPathEncode(string s) { #if NET_4_0 return HttpEncoder.Current.UrlPathEncode (s); #else return HttpEncoder.UrlPathEncode(s); #endif } public static NameValueCollection ParseQueryString(string query) { return ParseQueryString(query, Encoding.UTF8); } public static NameValueCollection ParseQueryString(string query, bool caseSensitive) { return ParseQueryString(query, Encoding.UTF8, caseSensitive); } public static NameValueCollection ParseQueryString(string query, Encoding encoding) { return ParseQueryString(query, encoding, StaticConfiguration.CaseSensitive); } public static NameValueCollection ParseQueryString(string query, Encoding encoding, bool caseSensitive) { if (query == null) throw new ArgumentNullException("query"); if (encoding == null) throw new ArgumentNullException("encoding"); if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) return new HttpQSCollection(caseSensitive); if (query[0] == '?') query = query.Substring(1); NameValueCollection result = new HttpQSCollection(caseSensitive); ParseQueryString(query, encoding, result); return result; } internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result) { if (query.Length == 0) return; var decoded = HtmlDecode(query); var segments = decoded.Split(new[] {'&'}, StringSplitOptions.None); foreach (var segment in segments) { var keyValuePair = ParseQueryStringSegment(segment, encoding); if (!Equals(keyValuePair, default(KeyValuePair))) result.Add(keyValuePair.Key, keyValuePair.Value); } } private static KeyValuePair ParseQueryStringSegment(string segment, Encoding encoding) { if (String.IsNullOrWhiteSpace(segment)) return default(KeyValuePair); var indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { var decoded = UrlDecode(segment, encoding); return new KeyValuePair(decoded, decoded); } var key = UrlDecode(segment.Substring(0, indexOfEquals), encoding); var length = (segment.Length - indexOfEquals) - 1; var value = UrlDecode(segment.Substring(indexOfEquals + 1, length), encoding); return new KeyValuePair(key, value); } #endregion // Methods } } ================================================ FILE: src/Nancy/Helpers/ProxyNancyReferenceProber.cs ================================================ #if !CORE namespace Nancy.Helpers { using System; using System.Reflection; using Nancy.Extensions; /// /// Utility class used to probe assembly references. /// /// /// Because this class inherits from it can be used across different . /// internal class ProxyNancyReferenceProber : MarshalByRefObject { /// /// Determines if the assembly has a reference (dependency) upon another one. /// /// The name of the assembly that will be tested. /// The reference assembly name. /// A boolean value indicating if there is a reference. public bool HasReference(AssemblyName assemblyNameForProbing, AssemblyName referenceAssemblyName) { var assemblyForInspection = Assembly.ReflectionOnlyLoad(assemblyNameForProbing.Name); return assemblyForInspection.IsReferencing(referenceAssemblyName); } } } #endif ================================================ FILE: src/Nancy/Helpers/ReflectionUtils.cs ================================================ #region License // 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. #endregion namespace Nancy.Helpers { using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; internal static class ReflectionUtils { public static bool IsInstantiatableType(Type t) { if (t == null) throw new ArgumentNullException("t"); if (t.GetTypeInfo().IsAbstract || t.GetTypeInfo().IsInterface || t.IsArray) return false; if (t.GetTypeInfo().IsGenericType) return false; if (!HasDefaultConstructor(t)) return false; return true; } public static bool HasDefaultConstructor(Type t) { if (t == null) { throw new ArgumentNullException("t"); } var hasDefaultConstructor = t.GetTypeInfo().DeclaredConstructors.Any(ctor => !ctor.GetParameters().Any()); return hasDefaultConstructor; } public static bool IsAssignable(Type to, Type from) { if (to == null) { throw new ArgumentNullException("to"); } if (to.IsAssignableFrom(from)) { return true; } if (to.GetTypeInfo().IsGenericType && from.GetTypeInfo().IsGenericTypeDefinition) { return to.IsAssignableFrom(from.MakeGenericType(to.GetGenericArguments())); } return false; } public static bool IsSubClass(Type type, Type check) { if (type == null || check == null) { return false; } if (type == check) { return true; } if (check.GetTypeInfo().IsInterface) { foreach (Type t in type.GetInterfaces()) { if (IsSubClass(t, check)) return true; } } if (type.GetTypeInfo().IsGenericType && !type.GetTypeInfo().IsGenericTypeDefinition) { if (IsSubClass(type.GetGenericTypeDefinition(), check)) return true; } return IsSubClass(type.GetTypeInfo().BaseType, check); } static readonly Type GenericListType = typeof(List<>); /// /// Gets the type of the typed list's items. /// /// The type. /// The type of the typed list's items. public static Type GetTypedListItemType(Type type) { if (type == null) throw new ArgumentNullException("type"); if (type.IsArray) return type.GetElementType(); else if (type.GetTypeInfo().IsGenericType && GenericListType.IsAssignableFrom(type.GetGenericTypeDefinition())) { return type.GetGenericArguments()[0]; } else { throw new Exception("Bad type"); } } public static Type GetTypedDictionaryValueType(Type type) { if (type == null) { throw new ArgumentNullException("type"); } var genDictType = GetGenericDictionary(type); if (genDictType != null) { return genDictType.GetGenericArguments()[1]; } else if (typeof(IDictionary).IsAssignableFrom(type)) { return null; } else { throw new Exception("Bad type"); } } static readonly Type GenericDictionaryType = typeof(IDictionary<,>); public static Type GetGenericDictionary(Type type) { if (type.GetTypeInfo().IsGenericType && GenericDictionaryType.IsAssignableFrom(type.GetGenericTypeDefinition())) { return type; } var ifaces = type.GetInterfaces(); if (ifaces != null) { for (int i = 0; i < ifaces.Length; i++) { Type current = GetGenericDictionary(ifaces[i]); if (current != null) return current; } } return null; } public static Type GetMemberUnderlyingType(MemberInfo member) { if (member is FieldInfo) { return ((FieldInfo)member).FieldType; } else if (member is PropertyInfo) { return ((PropertyInfo)member).PropertyType; } else if (member is EventInfo) { return ((EventInfo)member).EventHandlerType; } else { throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member"); } } /// /// Determines whether the member is an indexed property. /// /// The member. /// /// true if the member is an indexed property; otherwise, false. /// public static bool IsIndexedProperty(MemberInfo member) { if (member == null) { throw new ArgumentNullException("member"); } var propertyInfo = member as PropertyInfo; if (propertyInfo != null) { return IsIndexedProperty(propertyInfo); } else { return false; } } /// /// Determines whether the property is an indexed property. /// /// The property. /// /// true if the property is an indexed property; otherwise, false. /// public static bool IsIndexedProperty(PropertyInfo property) { if (property == null) { throw new ArgumentNullException("property"); } return (property.GetIndexParameters().Length > 0); } /// /// Gets the member's value on the object. /// /// The member. /// The target object. /// The member's value on the object. public static object GetMemberValue(MemberInfo member, object target) { if (member is FieldInfo) { return ((FieldInfo)member).GetValue(target); } else if (member is PropertyInfo) { try { return ((PropertyInfo)member).GetValue(target, null); } catch (TargetParameterCountException e) { throw new ArgumentException("MemberInfo has index parameters", "member", e); } } throw new ArgumentException("MemberInfo is not of type FieldInfo or PropertyInfo", "member"); } /// /// Sets the member's value on the target object. /// /// The member. /// The target. /// The value. public static void SetMemberValue(MemberInfo member, object target, object value) { if (member is FieldInfo) { ((FieldInfo)member).SetValue(target, value); return; } else if (member is PropertyInfo) { ((PropertyInfo)member).SetValue(target, value, null); return; } throw new ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", "member"); } /// /// Determines whether the specified MemberInfo can be read. /// /// The MemberInfo to determine whether can be read. /// /// true if the specified MemberInfo can be read; otherwise, false. /// public static bool CanReadMemberValue(MemberInfo member) { if (member is FieldInfo) { return true; } else if (member is PropertyInfo) { return ((PropertyInfo)member).CanRead; } return false; } /// /// Determines whether the specified MemberInfo can be set. /// /// The MemberInfo to determine whether can be set. /// /// true if the specified MemberInfo can be set; otherwise, false. /// public static bool CanSetMemberValue(MemberInfo member) { if (member is FieldInfo) { return true; } else if (member is PropertyInfo) { return ((PropertyInfo)member).CanWrite; } return false; } public static IEnumerable GetFieldsAndProperties(Type type, BindingFlags bindingAttr) { MemberInfo[] members = type.GetFields(bindingAttr); for (int i = 0; i < members.Length; i++) { yield return members[i]; } members = type.GetProperties(bindingAttr); for (int i = 0; i < members.Length; i++) { yield return members[i]; } } } } ================================================ FILE: src/Nancy/Helpers/TaskHelpers.cs ================================================ namespace Nancy.Helpers { using System; using System.Threading.Tasks; /// /// Convenience class with helper methods for . /// public static class TaskHelpers { /// /// The completed task /// public static readonly Task CompletedTask = Task.FromResult(null); /// /// Gets the faulted task. /// /// Type for /// The exception. /// The faulted public static Task GetFaultedTask(Exception exception) { var tcs = new TaskCompletionSource(); tcs.SetException(exception); return tcs.Task; } } } ================================================ FILE: src/Nancy/HttpFile.cs ================================================ namespace Nancy { using System.IO; /// /// Represents a file that was captured in a HTTP multipart/form-data request /// public class HttpFile { /// /// Initializes a new instance of the class, /// using the provided . /// /// The that contains the file information. public HttpFile(HttpMultipartBoundary boundary) : this(boundary.ContentType, boundary.Filename, boundary.Value, boundary.Name) { } /// /// Initializes a new instance of the class, /// using the provided values /// /// The content type of the file. /// The name of the file. /// The content of the file. /// The name of the field that uploaded the file. public HttpFile(string contentType, string name, Stream value, string key) { this.ContentType = contentType; this.Name = name; this.Value = value; this.Key = key; } /// /// Gets or sets the type of the content. /// /// A containing the content type of the file. public string ContentType { get; private set; } /// /// Gets or sets the name of the file. /// /// A containing the name of the file. public string Name { get; private set; } /// /// Gets or sets the form element name of this file. /// /// A containing the key. public string Key { get; private set; } /// /// Gets or sets the value stream. /// /// A containing the contents of the file. /// This is a instance that sits ontop of the request stream. public Stream Value { get; private set; } } } ================================================ FILE: src/Nancy/HttpLink.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Nancy.Responses.Negotiation; /// /// Represents one of possibly many RFC 5988 HTTP Links contained in a . /// public class HttpLink : IEquatable { private static readonly HttpLinkParameterComparer ParameterComparer = new HttpLinkParameterComparer(); private readonly IDictionary parameters; private readonly Uri targetUri; /// /// Initializes a new instance of the class. /// /// The target URI of the link. /// The relation that identifies the semantics of the link. /// or public HttpLink(string targetUri, string relation) : this(ParseUri(targetUri), relation, string.Empty, string.Empty) { } /// /// Initializes a new instance of the class. /// /// The target URI of the link. /// The relation that identifies the semantics of the link. /// The optional type parameter is a hint indicating what the media type of the result of dereferencing the link should be. Note that this is only a hint; for example, it does not override the HTTP Content-Type header of a HTTP response obtained by actually following the link. /// or public HttpLink(string targetUri, string relation, string type) : this(ParseUri(targetUri), relation, type, string.Empty) { } /// /// Initializes a new instance of the class. /// /// The target URI of the link. /// The relation that identifies the semantics of the link. /// The optional type parameter is a hint indicating what the media type of the result of dereferencing the link should be. Note that this is only a hint; for example, it does not override the HTTP Content-Type header of a HTTP response obtained by actually following the link. /// The optional title parameter is used to label the destination of a link such that it can be used as a human-readable identifier (e.g., a menu entry) in the language indicated by the HTTP Content-Language header (if present). /// or public HttpLink(string targetUri, string relation, string type, string title) : this(ParseUri(targetUri), relation, type, title) { } /// /// Initializes a new instance of the class. /// /// The target URI of the link. /// The relation that identifies the semantics of the link. /// The optional type parameter is a hint indicating what the media type of the result of dereferencing the link should be. Note that this is only a hint; for example, it does not override the HTTP Content-Type header of a HTTP response obtained by actually following the link. /// The optional title parameter is used to label the destination of a link such that it can be used as a human-readable identifier (e.g., a menu entry) in the language indicated by the HTTP Content-Language header (if present). /// or public HttpLink(Uri targetUri, string relation, string type, string title) { if (targetUri == null) { throw new ArgumentNullException("targetUri"); } if (relation == null) { throw new ArgumentNullException("relation"); } this.parameters = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "rel", HttpLinkRelation.Parse(relation) } }; this.targetUri = targetUri; if (!string.IsNullOrWhiteSpace(type)) { this.parameters.Add("type", new MediaRange(type)); } if (!string.IsNullOrEmpty(title)) { this.parameters.Add("title", title); } } /// /// The dictionary of parameters associated with the link. /// public IDictionary Parameters { get { return this.parameters; } } /// /// The relation that identifies the semantics of the link. /// public HttpLinkRelation Relation { get { return this.GetParameterValue("rel"); } } /// /// Gets the target URI. /// public Uri TargetUri { get { return this.targetUri; } } /// /// The optional title parameter is used to label the destination of a link such that it can be used as a human-readable identifier (e.g., a menu entry) in the language indicated by the HTTP Content-Language header (if present). /// public string Title { get { return this.GetParameterValue("title"); } } /// /// The optional type parameter is a hint indicating what the media type of the result of dereferencing the link should be. Note that this is only a hint; for example, it does not override the HTTP Content-Type header of a HTTP response obtained by actually following the link. /// public MediaRange Type { get { return this.GetParameterValue("type"); } } /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(HttpLink other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } if (!Equals(this.targetUri, other.targetUri)) { return false; } foreach (var parameter in other.Parameters) { object parameterValue; if (!this.parameters.TryGetValue(parameter.Key, out parameterValue)) { return false; } if (parameterValue == null || parameter.Value == null) { continue; } if (!parameterValue.Equals(parameter.Value)) { return false; } } return true; } /// /// Determines whether the specified , is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object other) { return other is HttpLink && this.Equals((HttpLink)other); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { unchecked { var p = 0; foreach (var parameter in this.parameters) { p ^= StringComparer.OrdinalIgnoreCase.GetHashCode(parameter.Key); p ^= parameter.Value != null ? parameter.Value.GetHashCode() : 0; } var u = this.targetUri != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(this.targetUri.ToString()) : 0; return (p * 397) ^ u; } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { var linkBuilder = new StringBuilder(); linkBuilder.Append('<'); linkBuilder.Append(this.TargetUri); linkBuilder.Append('>'); var parameters = this.parameters .Where(parameter => !string.IsNullOrWhiteSpace(parameter.Key)) .OrderBy(parameter => parameter.Key, ParameterComparer); foreach (var parameter in parameters) { linkBuilder.Append("; "); linkBuilder.Append(parameter.Key); if (parameter.Value == null) { continue; } linkBuilder.Append('='); linkBuilder.Append('"'); linkBuilder.Append(parameter.Value); linkBuilder.Append('"'); } return linkBuilder.ToString(); } private T GetParameterValue(string parameterName) { object parameterValue; if (!this.parameters.TryGetValue(parameterName, out parameterValue)) { return default(T); } return (T)parameterValue; } /// /// Parses the specified string into a . /// /// The URI string. /// /// uri private static Uri ParseUri(string uri) { Uri parsedUri; // Mono workaround. See http://www.mono-project.com/docs/faq/known-issues/urikind-relativeorabsolute/ for details. @asbjornu var uriKind = uri.StartsWith("/") ? UriKind.Relative : UriKind.RelativeOrAbsolute; if (!Uri.TryCreate(uri, uriKind, out parsedUri)) { throw new ArgumentException(string.Format("Can't parse '{0}' into an URI.", uri), "uri"); } return parsedUri; } private class HttpLinkParameterComparer : IComparer { public int Compare(string x, string y) { if (ReferenceEquals(x, y)) { return 0; } if (ReferenceEquals(x, null)) { return -1; } if (x == "rel") { return -1; } return string.Compare(x, y, StringComparison.Ordinal); } } } } ================================================ FILE: src/Nancy/HttpLinkBuilder.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Text; /// /// Builds an RFC 5988 Link HTTP header as a of objects. /// /// public class HttpLinkBuilder : List { private readonly List additionalLinks; /// /// Initializes a new instance of the class. /// public HttpLinkBuilder() { this.additionalLinks = new List(); } /// /// Adds the specified link to the builder. /// /// The link to add to the builder. public void Add(string link) { if (link == null) { throw new ArgumentNullException("link"); } this.additionalLinks.Add(link); } /// /// Returns a that represents an RFC 5988 Link HTTP header. /// /// /// A that represents an RFC 5988 Link HTTP header. /// public override string ToString() { var sb = new StringBuilder(); foreach (var link in this) { if (sb.Length > 0) { sb.Append(", "); } sb.Append(link); } foreach (var link in this.additionalLinks) { if (sb.Length > 0) { sb.Append(", "); } sb.Append(link); } return sb.ToString(); } } } ================================================ FILE: src/Nancy/HttpLinkRelation.cs ================================================ namespace Nancy { using System; /// /// The relation that identifies the semantics of a contained in an RFC 5988 Link HTTP header, /// as built by the . /// /// public class HttpLinkRelation : Uri, IEquatable { /// /// The URI prefix to use for IANA registered link relations. /// public static readonly Uri IanaLinkRelationPrefix = new Uri("http://www.iana.org/assignments/relation/"); private readonly Uri prefix; private readonly string value; /// /// Initializes a new instance of the class. /// /// The relation. public HttpLinkRelation(string relation) : this(Parse(relation).Prefix, Parse(relation).Value) { } /// /// Initializes a new instance of the class. /// /// The prefix. /// The value. public HttpLinkRelation(Uri prefix, string value) : base(prefix + value) { this.prefix = prefix; this.value = value; } /// /// Gets the prefix for the link relation. Will be set to /// if the is a relative one. /// /// /// The prefix for the link relation. /// public Uri Prefix { get { return this.prefix; } } /// /// Gets the link relation value. /// /// /// The link relation value. /// public string Value { get { return this.value; } } /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(HttpLinkRelation other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return StringComparer.OrdinalIgnoreCase.Equals(this.ToString(), other.ToString()); } /// /// Determines whether the specified , is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object other) { return other is HttpLinkRelation && this.Equals((HttpLinkRelation)other); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { unchecked { var p = this.prefix != null ? this.prefix.GetHashCode() : 0; var v = this.value != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(this.value) : 0; return (p * 397) ^ v; } } /// /// Parses the specified link name into an absolute . /// Will be prefixed with if the is /// a relative value. /// /// The link relation name. /// /// A new instance of from the parsed link value. /// /// /// public static HttpLinkRelation Parse(string relation) { if (string.IsNullOrEmpty(relation)) { throw new ArgumentNullException("relation"); } Uri parsedRelation; if (TryCreate(relation, UriKind.Absolute, out parsedRelation)) { if (parsedRelation.AbsolutePath.Length < 2 || !parsedRelation.AbsolutePath.Contains("/") || parsedRelation.AbsolutePath.EndsWith("/")) { throw new FormatException(string.Format("The link relation '{0}' is invalid.", relation)); } var slashIndex = relation.LastIndexOf('/'); var prefix = relation.Substring(0, slashIndex + 1); var value = relation.Substring(slashIndex + 1); var prefixUri = new Uri(prefix); return new HttpLinkRelation(prefixUri, value); } if (TryCreate(IanaLinkRelationPrefix + relation, UriKind.Absolute, out parsedRelation)) { return new HttpLinkRelation(IanaLinkRelationPrefix, relation); } throw new FormatException(string.Format("The link relation '{0}' is invalid.", relation)); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this.prefix == IanaLinkRelationPrefix ? this.Value : base.ToString(); } } } ================================================ FILE: src/Nancy/HttpMultipart.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; /// /// Retrieves instances from a request stream. /// public class HttpMultipart { private const byte LF = (byte)'\n'; private readonly byte[] boundaryAsBytes; private readonly HttpMultipartBuffer readBuffer; private readonly Stream requestStream; private readonly byte[] closingBoundaryAsBytes; /// /// Initializes a new instance of the class. /// /// The request stream to parse. /// The boundary marker to look for. public HttpMultipart(Stream requestStream, string boundary) { this.requestStream = requestStream; this.boundaryAsBytes = GetBoundaryAsBytes(boundary, false); this.closingBoundaryAsBytes = GetBoundaryAsBytes(boundary, true); this.readBuffer = new HttpMultipartBuffer(this.boundaryAsBytes, this.closingBoundaryAsBytes); } /// /// Gets the instances from the request stream. /// /// An instance, containing the found instances. public IEnumerable GetBoundaries() { return (from boundaryStream in this.GetBoundarySubStreams() select new HttpMultipartBoundary(boundaryStream)).ToList(); } private IEnumerable GetBoundarySubStreams() { var boundarySubStreams = new List(); var boundaryStart = this.GetNextBoundaryPosition(); var found = 0; while (MultipartIsNotCompleted(boundaryStart) && found < StaticConfiguration.RequestQueryFormMultipartLimit) { var boundaryEnd = this.GetNextBoundaryPosition(); boundarySubStreams.Add(new HttpMultipartSubStream( this.requestStream, boundaryStart, this.GetActualEndOfBoundary(boundaryEnd))); boundaryStart = boundaryEnd; found++; } return boundarySubStreams; } private bool MultipartIsNotCompleted(long boundaryPosition) { return boundaryPosition > -1 && !this.readBuffer.IsClosingBoundary; } //we add two because or the \r\n before the boundary private long GetActualEndOfBoundary(long boundaryEnd) { if (this.CheckIfFoundEndOfStream()) { return this.requestStream.Position - (this.readBuffer.Length + 2); } return boundaryEnd - (this.readBuffer.Length + 2); } private bool CheckIfFoundEndOfStream() { return this.requestStream.Position.Equals(this.requestStream.Length); } private static byte[] GetBoundaryAsBytes(string boundary, bool closing) { var boundaryBuilder = new StringBuilder(); boundaryBuilder.Append("--"); boundaryBuilder.Append(boundary); if(closing) { boundaryBuilder.Append("--"); } else { boundaryBuilder.Append('\r'); boundaryBuilder.Append('\n'); } var bytes = Encoding.ASCII.GetBytes(boundaryBuilder.ToString()); return bytes; } private long GetNextBoundaryPosition() { this.readBuffer.Reset(); while(true) { var byteReadFromStream = this.requestStream.ReadByte(); if (byteReadFromStream == -1) { return -1; } this.readBuffer.Insert((byte)byteReadFromStream); if (this.readBuffer.IsFull && (this.readBuffer.IsBoundary || this.readBuffer.IsClosingBoundary)) { return this.requestStream.Position; } if (byteReadFromStream.Equals(LF) || this.readBuffer.IsFull) { this.readBuffer.Reset(); } } } } } ================================================ FILE: src/Nancy/HttpMultipartBoundary.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; /// /// Represents the content boundary of a HTTP multipart/form-data boundary in a stream. /// public class HttpMultipartBoundary { private const byte LF = (byte)'\n'; private const byte CR = (byte)'\r'; /// /// Initializes a new instance of the class. /// /// The stream that contains the boundary information. public HttpMultipartBoundary(HttpMultipartSubStream boundaryStream) { this.Value = boundaryStream; this.ExtractHeaders(); } /// /// Gets the contents type of the boundary value. /// /// A containing the name of the value if it is available; otherwise . public string ContentType { get; private set; } /// /// Gets or the filename for the boundary value. /// /// A containing the filename value if it is available; otherwise . /// This is the RFC2047 decoded value of the filename attribute of the Content-Disposition header. public string Filename { get; private set; } /// /// Gets name of the boundary value. /// /// This is the RFC2047 decoded value of the name attribute of the Content-Disposition header. public string Name { get; private set; } /// /// A stream containing the value of the boundary. /// /// This is the RFC2047 decoded value of the Content-Type header. public HttpMultipartSubStream Value { get; private set; } private void ExtractHeaders() { while(true) { var header = ReadLineFromStream(this.Value); if (string.IsNullOrEmpty(header)) { break; } if (header.StartsWith("Content-Disposition", StringComparison.CurrentCultureIgnoreCase)) { this.Name = Regex.Match(header, @"name=""?(?[^\""]*)", RegexOptions.IgnoreCase).Groups["name"].Value; this.Filename = Regex.Match(header, @"filename\*?=""?(?[^\"";]*)", RegexOptions.IgnoreCase).Groups["filename"].Value; if (this.Filename.StartsWith("utf-8''", StringComparison.CurrentCultureIgnoreCase)) { this.Filename = Uri.UnescapeDataString(this.Filename.Substring(7)); } } if (header.StartsWith("Content-Type", StringComparison.OrdinalIgnoreCase)) { this.ContentType = header.Split(new[] { ' ' }).Last().Trim(); } } this.Value.PositionStartAtCurrentLocation(); } private static string ReadLineFromStream(Stream stream) { var readBuffer = new List(); while (true) { var byteReadFromStream = stream.ReadByte(); if (byteReadFromStream == -1) { return null; } if (byteReadFromStream.Equals(LF)) { break; } readBuffer.Add((byte) byteReadFromStream); } return Encoding.UTF8.GetString(readBuffer.ToArray()).Trim((char) CR); } } } ================================================ FILE: src/Nancy/HttpMultipartBuffer.cs ================================================ namespace Nancy { using System; using System.Linq; /// /// A buffer that is used to locate a HTTP multipart/form-data boundary in a stream. /// public class HttpMultipartBuffer { private readonly byte[] boundaryAsBytes; private readonly byte[] closingBoundaryAsBytes; private readonly byte[] buffer; private int position; /// /// Initializes a new instance of the class, with /// the provided and . /// /// The boundary as a byte-array. /// The closing boundary as byte-array public HttpMultipartBuffer(byte[] boundaryAsBytes, byte[] closingBoundaryAsBytes) { this.boundaryAsBytes = boundaryAsBytes; this.closingBoundaryAsBytes = closingBoundaryAsBytes; this.buffer = new byte[this.boundaryAsBytes.Length]; } /// /// Gets a value indicating whether the buffer contains the same values as the boundary. /// /// if buffer contains the same values as the boundary; otherwise, . public bool IsBoundary { get { return this.buffer.SequenceEqual(this.boundaryAsBytes); } } /// /// Gets a value indicating whether this instance is closing boundary. /// /// /// if this instance is closing boundary; otherwise, . /// public bool IsClosingBoundary { get { return this.buffer.SequenceEqual(this.closingBoundaryAsBytes); } } /// /// Gets a value indicating whether this buffer is full. /// /// if buffer is full; otherwise, . public bool IsFull { get { return this.position.Equals(this.buffer.Length); } } /// /// Gets the number of bytes that can be stored in the buffer. /// /// The number of bytes that can be stored in the buffer. public int Length { get { return this.buffer.Length; } } /// /// Resets the buffer so that inserts happens from the start again. /// /// This does not clear any previously written data, just resets the buffer position to the start. Data that is inserted after Reset has been called will overwrite old data. public void Reset() { this.position = 0; } /// /// Inserts the specified value into the buffer and advances the internal position. /// /// The value to insert into the buffer. /// This will throw an is you attempt to call insert more times then the of the buffer and was not invoked. public void Insert(byte value) { this.buffer[this.position++] = value; } } } ================================================ FILE: src/Nancy/HttpMultipartSubStream.cs ================================================ namespace Nancy { using System; using System.IO; /// /// A decorator stream that sits on top of an existing stream and appears as a unique stream. /// public class HttpMultipartSubStream : Stream { private readonly Stream stream; private long start; private readonly long end; private long position; /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The stream to create the sub-stream ontop of. /// The start offset on the parent stream where the sub-stream should begin. /// The end offset on the parent stream where the sub-stream should end. public HttpMultipartSubStream(Stream stream, long start, long end) { this.stream = stream; this.start = start; this.position = start; this.end = end; } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports reading. /// /// if the stream supports reading; otherwise, . public override bool CanRead { get { return true; } } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. /// /// if the stream supports seeking; otherwise, . public override bool CanSeek { get { return true; } } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. /// /// if the stream supports writing; otherwise, . public override bool CanWrite { get { return false; } } /// /// When overridden in a derived class, gets the length in bytes of the stream. /// /// A long value representing the length of the stream in bytes. /// A class derived from Stream does not support seeking. Methods were called after the stream was closed. public override long Length { get { return (this.end - this.start); } } /// /// When overridden in a derived class, gets or sets the position within the current stream. /// /// /// The current position within the stream. /// /// An I/O error occurs. The stream does not support seeking. Methods were called after the stream was closed. 1 public override long Position { get { return this.position - this.start; } set { this.position = this.Seek(value, SeekOrigin.Begin); } } private long CalculateSubStreamRelativePosition(SeekOrigin origin, long offset) { var subStreamRelativePosition = 0L; switch (origin) { case SeekOrigin.Begin: subStreamRelativePosition = this.start + offset; break; case SeekOrigin.Current: subStreamRelativePosition = this.position + offset; break; case SeekOrigin.End: subStreamRelativePosition = this.end + offset; break; } return subStreamRelativePosition; } /// /// Sets the position of the stream as the start point. /// public void PositionStartAtCurrentLocation() { this.start = this.stream.Position; } /// /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// /// In the type this method is implemented as no-op. public override void Flush() { } /// /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. /// The zero-based byte offset in at which to begin storing the data read from the current stream. /// The maximum number of bytes to be read from the current stream. public override int Read(byte[] buffer, int offset, int count) { if (count > (this.end - this.position)) { count = (int)(this.end - this.position); } if (count <= 0) { return 0; } this.stream.Position = this.position; var bytesReadFromStream = this.stream.Read(buffer, offset, count); this.RepositionAfterRead(bytesReadFromStream); return bytesReadFromStream; } /// /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. public override int ReadByte() { if (this.position >= this.end) { return -1; } this.stream.Position = this.position; var byteReadFromStream = this.stream.ReadByte(); this.RepositionAfterRead(1); return byteReadFromStream; } private void RepositionAfterRead(int bytesReadFromStream) { if (bytesReadFromStream == -1) { this.position = this.end; } else { this.position += bytesReadFromStream; } } /// /// When overridden in a derived class, sets the position within the current stream. /// /// The new position within the current stream. /// A byte offset relative to the parameter. /// A value of type indicating the reference point used to obtain the new position. public override long Seek(long offset, SeekOrigin origin) { var subStreamRelativePosition = this.CalculateSubStreamRelativePosition(origin, offset); this.ThrowExceptionIsPositionIsOutOfBounds(subStreamRelativePosition); this.position = this.stream.Seek(subStreamRelativePosition, SeekOrigin.Begin); return this.position; } /// /// When overridden in a derived class, sets the length of the current stream. /// /// The desired length of the current stream in bytes. /// This will always throw a for the type. public override void SetLength(long value) { throw new InvalidOperationException(); } /// /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. /// /// An array of bytes. This method copies bytes from to the current stream. /// The zero-based byte offset in at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. /// This will always throw a for the type. public override void Write(byte[] buffer, int offset, int count) { throw new InvalidOperationException(); } private void ThrowExceptionIsPositionIsOutOfBounds(long subStreamRelativePosition) { if (subStreamRelativePosition < 0 || subStreamRelativePosition > this.end) throw new InvalidOperationException(); } } } ================================================ FILE: src/Nancy/HttpStatusCode.cs ================================================ namespace Nancy { /// /// HTTP Status Codes /// /// The values are based on the list found at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes public enum HttpStatusCode { /// /// 100 Continue /// Continue = 100, /// /// 101 SwitchingProtocols /// SwitchingProtocols = 101, /// /// 102 Processing /// Processing = 102, /// /// 103 Checkpoint /// Checkpoint = 103, /// /// 200 OK /// OK = 200, /// /// 201 Created /// Created = 201, /// /// 202 Accepted /// Accepted = 202, /// /// 203 NonAuthoritativeInformation /// NonAuthoritativeInformation = 203, /// /// 204 NoContent /// NoContent = 204, /// /// 205 ResetContent /// ResetContent = 205, /// /// 206 PartialContent /// PartialContent = 206, /// /// 207 MultipleStatus /// MultipleStatus = 207, /// /// 226 IMUsed /// IMUsed = 226, /// /// 300 MultipleChoices /// MultipleChoices = 300, /// /// 301 MovedPermanently /// MovedPermanently = 301, /// /// 302 Found /// Found = 302, /// /// 303 SeeOther /// SeeOther = 303, /// /// 304 NotModified /// NotModified = 304, /// /// 305 UseProxy /// UseProxy = 305, /// /// 306 SwitchProxy /// SwitchProxy = 306, /// /// 307 TemporaryRedirect /// TemporaryRedirect = 307, /// /// 308 ResumeIncomplete /// ResumeIncomplete = 308, /// /// 400 BadRequest /// BadRequest = 400, /// /// 401 Unauthorized /// Unauthorized = 401, /// /// 402 PaymentRequired /// PaymentRequired = 402, /// /// 403 Forbidden /// Forbidden = 403, /// /// 404 NotFound /// NotFound = 404, /// /// 405 MethodNotAllowed /// MethodNotAllowed = 405, /// /// 406 NotAcceptable /// NotAcceptable = 406, /// /// 407 ProxyAuthenticationRequired /// ProxyAuthenticationRequired = 407, /// /// 408 RequestTimeout /// RequestTimeout = 408, /// /// 409 Conflict /// Conflict = 409, /// /// 410 Gone /// Gone = 410, /// /// 411 LengthRequired /// LengthRequired = 411, /// /// 412 PreconditionFailed /// PreconditionFailed = 412, /// /// 413 RequestEntityTooLarge /// RequestEntityTooLarge = 413, /// /// 414 RequestUriTooLong /// RequestUriTooLong = 414, /// /// 415 UnsupportedMediaType /// UnsupportedMediaType = 415, /// /// 416 RequestedRangeNotSatisfiable /// RequestedRangeNotSatisfiable = 416, /// /// 417 ExpectationFailed /// ExpectationFailed = 417, /// /// 418 ImATeapot /// ImATeapot = 418, /// /// 420 Enhance Your Calm /// EnhanceYourCalm = 420, /// /// 422 UnprocessableEntity /// UnprocessableEntity = 422, /// /// 423 Locked /// Locked = 423, /// /// 424 FailedDependency /// FailedDependency = 424, /// /// 425 UnorderedCollection /// UnorderedCollection = 425, /// /// 426 UpgradeRequired /// UpgradeRequired = 426, /// /// 429 Too Many Requests /// TooManyRequests = 429, /// /// 444 NoResponse /// NoResponse = 444, /// /// 449 RetryWith /// RetryWith = 449, /// /// 450 BlockedByWindowsParentalControls /// BlockedByWindowsParentalControls = 450, /// /// 451 UnavailableForLegalReasons /// UnavailableForLegalReasons = 451, /// /// 499 ClientClosedRequest /// ClientClosedRequest = 499, /// /// 500 InternalServerError /// InternalServerError = 500, /// /// 501 NotImplemented /// NotImplemented = 501, /// /// 502 BadGateway /// BadGateway = 502, /// /// 503 ServiceUnavailable /// ServiceUnavailable = 503, /// /// 504 GatewayTimeout /// GatewayTimeout = 504, /// /// 505 HttpVersionNotSupported /// HttpVersionNotSupported = 505, /// /// 506 VariantAlsoNegotiates /// VariantAlsoNegotiates = 506, /// /// 507 InsufficientStorage /// InsufficientStorage = 507, /// /// 509 BandwidthLimitExceeded /// BandwidthLimitExceeded = 509, /// /// 510 NotExtended /// NotExtended = 510 } } ================================================ FILE: src/Nancy/IAssemblyCatalog.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.Reflection; /// /// Defines the functionality of an assembly catalog. /// public interface IAssemblyCatalog { /// /// Gets all instances in the catalog. /// /// An of instances. IReadOnlyCollection GetAssemblies(); } } ================================================ FILE: src/Nancy/IHideObjectMembers.cs ================================================ namespace Nancy { using System; using System.ComponentModel; /// /// Helper interface used to hide the base members from the fluent API to make it much cleaner /// in Visual Studio intellisense. /// [EditorBrowsable(EditorBrowsableState.Never)] public interface IHideObjectMembers { /// /// Hides the method. /// [EditorBrowsable(EditorBrowsableState.Never)] bool Equals(object obj); /// /// Hides the method. /// [EditorBrowsable(EditorBrowsableState.Never)] int GetHashCode(); /// /// Hides the method. /// [EditorBrowsable(EditorBrowsableState.Never)] Type GetType(); /// /// Hides the method. /// [EditorBrowsable(EditorBrowsableState.Never)] string ToString(); } } ================================================ FILE: src/Nancy/INancyContextFactory.cs ================================================ namespace Nancy { /// /// Creates NancyContext instances /// public interface INancyContextFactory { /// /// Create a new NancyContext /// /// NancyContext instance NancyContext Create(Request request); } } ================================================ FILE: src/Nancy/INancyEngine.cs ================================================ namespace Nancy { using System; using System.Threading; using System.Threading.Tasks; using Nancy.Bootstrapper; /// /// Defines the functionality of an engine that can handle Nancy s. /// public interface INancyEngine : IDisposable { /// /// Factory for creating an instance for a incoming request. /// /// An instance. Func RequestPipelinesFactory { get; set; } /// /// Handles an incoming async. /// /// An instance, containing the information about the current request. /// Delegate to call before the request is processed /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// The task object representing the asynchronous operation. Task HandleRequest(Request request, Func preRequest, CancellationToken cancellationToken); } } ================================================ FILE: src/Nancy/INancyModule.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.ComponentModel; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Validation; using Nancy.ViewEngines; /// /// Nancy module base interface /// Defines all the properties / behaviour needed by Nancy internally /// public interface INancyModule { /// /// The post-request hook /// /// The post-request hook is called after the response is created by the route execution. /// It can be used to rewrite the response or add/remove items from the context. /// AfterPipeline After { get; set; } /// /// The pre-request hook /// /// The PreRequest hook is called prior to executing a route. If any item in the /// pre-request pipeline returns a response then the route is not executed and the /// response is returned. /// BeforePipeline Before { get; set; } /// /// The error hook /// /// The error hook is called if an exception is thrown at any time during executing /// the PreRequest hook, a route and the PostRequest hook. It can be used to set /// the response and/or finish any ongoing tasks (close database session, etc). /// ErrorPipeline OnError { get; set; } /// /// Gets or sets the current Nancy context /// A instance. NancyContext Context { get; set; } /// /// An extension point for adding support for formatting response contents. /// This property will always return because it acts as an extension point.Extension methods to this property should always return or one of the types that can implicitly be types into a . IResponseFormatter Response { get; set; } /// /// Gets or sets the model binder locator /// IModelBinderLocator ModelBinderLocator { get; set; } /// /// Gets or sets the model validation result /// [EditorBrowsable(EditorBrowsableState.Never)] ModelValidationResult ModelValidationResult { get; set; } /// /// Gets or sets the validator locator. /// IModelValidatorLocator ValidatorLocator { get; set; } /// /// Gets or sets an instance that represents the current request. /// An instance. Request Request { get; set; } /// /// The extension point for accessing the view engines in Nancy. /// An instance.This is automatically set by Nancy at runtime. IViewFactory ViewFactory { get; set; } /// /// Get the root path of the routes in the current module. /// A containing the root path of the module or if no root path should be used.All routes will be relative to this root path. string ModulePath { get; } /// /// Gets all declared routes by the module. /// A instance, containing all instances declared by the module. IEnumerable Routes { get; } /// /// Gets or sets the dynamic object used to locate text resources. /// dynamic Text { get; } /// /// Renders a view from inside a route handler. /// /// A instance that is used to determine which view that should be rendered. ViewRenderer View { get; } /// /// Used to negotiate the content returned based on Accepts header. /// /// A instance that is used to negotiate the content returned. Negotiator Negotiate { get; } } } ================================================ FILE: src/Nancy/INancyModuleCatalog.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; /// /// Catalog of instances. /// public interface INancyModuleCatalog { /// /// Get all NancyModule implementation instances - should be per-request lifetime /// /// The current context /// An instance containing instances. IEnumerable GetAllModules(NancyContext context); /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance INancyModule GetModule(Type moduleType, NancyContext context); } } ================================================ FILE: src/Nancy/IO/RequestStream.cs ================================================ namespace Nancy.IO { using System; using System.IO; using System.Threading.Tasks; using Nancy.Extensions; /// /// A decorator that can handle moving the stream out from memory and on to disk when the contents reaches a certain length. /// public class RequestStream : Stream { internal const int BufferSize = 4096; /// /// The default switchover threshold /// public static long DEFAULT_SWITCHOVER_THRESHOLD = 81920; private bool disableStreamSwitching; private readonly long thresholdLength; private bool isSafeToDisposeStream; private Stream stream; /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The expected length of the contents in the stream. /// The content length that will trigger the stream to be moved out of memory. /// if set to the stream will never explicitly be moved to disk. public RequestStream(long expectedLength, long thresholdLength, bool disableStreamSwitching) : this(null, expectedLength, thresholdLength, disableStreamSwitching) { } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The that should be handled by the request stream /// The expected length of the contents in the stream. /// if set to the stream will never explicitly be moved to disk. public RequestStream(Stream stream, long expectedLength, bool disableStreamSwitching) : this(stream, expectedLength, DEFAULT_SWITCHOVER_THRESHOLD, disableStreamSwitching) { } /// /// Initializes a new instance of the class, with /// the provided and . /// /// The expected length of the contents in the stream. /// if set to the stream will never explicitly be moved to disk. public RequestStream(long expectedLength, bool disableStreamSwitching) : this(null, expectedLength, DEFAULT_SWITCHOVER_THRESHOLD, disableStreamSwitching) { } /// /// Initializes a new instance of the class, with /// the provided , , and . /// /// The that should be handled by the request stream /// The expected length of the contents in the stream. /// The content length that will trigger the stream to be moved out of memory. /// if set to the stream will never explicitly be moved to disk. public RequestStream(Stream stream, long expectedLength, long thresholdLength, bool disableStreamSwitching) { this.thresholdLength = thresholdLength; this.disableStreamSwitching = disableStreamSwitching; this.stream = stream ?? this.CreateDefaultMemoryStream(expectedLength); ThrowExceptionIfCtorParametersWereInvalid(this.stream, expectedLength, this.thresholdLength); if (!this.MoveStreamOutOfMemoryIfExpectedLengthExceedSwitchLength(expectedLength)) { this.MoveStreamOutOfMemoryIfContentsLengthExceedThresholdAndSwitchingIsEnabled(); } if (!this.stream.CanSeek) { this.MoveToWritableStream(); } this.stream.Position = 0; } /// /// Finalizes an instance of the class. /// ~RequestStream() { this.Dispose(false); } private void MoveToWritableStream() { var sourceStream = this.stream; this.stream = new MemoryStream(BufferSize); sourceStream.CopyTo(this.stream); } /// /// Gets a value indicating whether the current stream supports reading. /// /// Always returns . public override bool CanRead { get { return true; } } /// /// Gets a value indicating whether the current stream supports seeking. /// /// Always returns . public override bool CanSeek { get { return this.stream.CanSeek; } } /// /// Gets a value that determines whether the current stream can time out. /// /// Always returns . public override bool CanTimeout { get { return false; } } /// /// Gets a value indicating whether the current stream supports writing. /// /// Always returns . public override bool CanWrite { get { return true; } } /// /// Gets the length in bytes of the stream. /// /// A long value representing the length of the stream in bytes. public override long Length { get { return this.stream.Length; } } /// /// Gets a value indicating whether the current stream is stored in memory. /// /// if the stream is stored in memory; otherwise, . /// The stream is moved to disk when either the length of the contents or expected content length exceeds the threshold specified in the constructor. public bool IsInMemory { get { return !(this.stream.GetType() == typeof(FileStream)); } } /// /// Gets or sets the position within the current stream. /// /// The current position within the stream. public override long Position { get { return this.stream.Position; } set { if (value < 0) throw new InvalidOperationException("The position of the stream cannot be set to less than zero."); if (value > this.Length) throw new InvalidOperationException("The position of the stream cannot exceed the length of the stream."); this.stream.Position = value; } } #if !NETSTANDARD1_6 /// /// Begins an asynchronous read operation. /// /// An that represents the asynchronous read, which could still be pending. /// The buffer to read the data into. /// The byte offset in at which to begin writing data read from the stream. /// The maximum number of bytes to read. /// An optional asynchronous callback, to be called when the read is complete. /// A user-provided object that distinguishes this particular asynchronous read request from other requests. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.stream.BeginRead(buffer, offset, count, callback, state); } /// /// Begins an asynchronous write operation. /// /// An that represents the asynchronous write, which could still be pending. /// The buffer to write data from. /// The byte offset in from which to begin writing. /// The maximum number of bytes to write. /// An optional asynchronous callback, to be called when the write is complete. /// A user-provided object that distinguishes this particular asynchronous write request from other requests. public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.stream.BeginWrite(buffer, offset, count, callback, state); } #endif /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (this.isSafeToDisposeStream) { if (this.stream != null) { this.stream.Dispose(); } var fileStream = this.stream as FileStream; if (fileStream != null) { DeleteTemporaryFile(fileStream.Name); } } base.Dispose(disposing); } #if !NETSTANDARD1_6 /// /// Waits for the pending asynchronous read to complete. /// /// /// The number of bytes read from the stream, between zero (0) and the number of bytes you requested. Streams return zero (0) only at the end of the stream, otherwise, they should block until at least one byte is available. /// /// The reference to the pending asynchronous request to finish. public override int EndRead(IAsyncResult asyncResult) { return this.stream.EndRead(asyncResult); } /// /// Ends an asynchronous write operation. /// /// A reference to the outstanding asynchronous I/O request. public override void EndWrite(IAsyncResult asyncResult) { this.stream.EndWrite(asyncResult); this.ShiftStreamToFileStreamIfNecessary(); } #endif /// /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// public override void Flush() { this.stream.Flush(); } /// /// Creates a new request stream from a stream. /// /// The stream. /// A request stream instance public static RequestStream FromStream(Stream stream) { return FromStream(stream, 0, DEFAULT_SWITCHOVER_THRESHOLD, false); } /// /// Creates a new request stream from a stream. /// /// The stream. /// The expected length. /// A request stream instance public static RequestStream FromStream(Stream stream, long expectedLength) { return FromStream(stream, expectedLength, DEFAULT_SWITCHOVER_THRESHOLD, false); } /// /// Creates a new request stream from a stream. /// /// The stream. /// The expected length. /// Length of the threshold. /// A request stream instance public static RequestStream FromStream(Stream stream, long expectedLength, long thresholdLength) { return FromStream(stream, expectedLength, thresholdLength, false); } /// /// Creates a new request stream from a stream. /// /// The stream. /// The expected length. /// if set to true [disable stream switching]. /// A request stream instance public static RequestStream FromStream(Stream stream, long expectedLength, bool disableStreamSwitching) { return FromStream(stream, expectedLength, DEFAULT_SWITCHOVER_THRESHOLD, disableStreamSwitching); } /// /// Creates a new request stream from a stream. /// /// The stream. /// The expected length. /// Length of the threshold. /// if set to true [disable stream switching]. /// A request stream instance public static RequestStream FromStream(Stream stream, long expectedLength, long thresholdLength, bool disableStreamSwitching) { return new RequestStream(stream, expectedLength, thresholdLength, disableStreamSwitching); } /// /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. /// The zero-based byte offset in at which to begin storing the data read from the current stream. /// The maximum number of bytes to be read from the current stream. public override int Read(byte[] buffer, int offset, int count) { return this.stream.Read(buffer, offset, count); } /// /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. public override int ReadByte() { return this.stream.ReadByte(); } /// /// Sets the position within the current stream. /// /// The new position within the current stream. /// A byte offset relative to the parameter. /// A value of type indicating the reference point used to obtain the new position. public override long Seek(long offset, SeekOrigin origin) { return this.stream.Seek(offset, origin); } /// /// Sets the length of the current stream. /// /// The desired length of the current stream in bytes. /// The stream does not support having its length set. /// This functionality is not supported by the type and will always throw . public override void SetLength(long value) { throw new NotSupportedException(); } /// /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. /// /// An array of bytes. This method copies bytes from to the current stream. /// The zero-based byte offset in at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. public override void Write(byte[] buffer, int offset, int count) { this.stream.Write(buffer, offset, count); this.ShiftStreamToFileStreamIfNecessary(); } private void ShiftStreamToFileStreamIfNecessary() { if (this.disableStreamSwitching) { return; } if (this.stream.Length >= this.thresholdLength) { // Close the stream here as closing it every time we call // MoveStreamContentsToFileStream causes an (ObjectDisposedException) // in NancyWcfGenericService - webRequest.UriTemplateMatch var old = this.stream; this.MoveStreamContentsToFileStream(); #if NETSTANDARD2_0 old.Dispose(); #else old.Close(); #endif } } private static FileStream CreateTemporaryFileStream() { // we could use Path.GetTempFilePath() but this is problematic on Windows and more so on Mono // symptoms will show when this method has been called > 65k times // see docs: https://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename(v=vs.110).aspx // comments on Win32 implementation: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364991(v=vs.85).aspx // mono implementation: https://github.com/mono/mono/blob/master/mcs/class/corlib/System.IO/Path.cs#L490 var filePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".tmp"); return new FileStream( filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 8192, StaticConfiguration.AllowFileStreamUploadAsync); } private Stream CreateDefaultMemoryStream(long expectedLength) { this.isSafeToDisposeStream = true; if (this.disableStreamSwitching || expectedLength < this.thresholdLength) { return new MemoryStream((int)expectedLength); } this.disableStreamSwitching = true; return CreateTemporaryFileStream(); } private static void DeleteTemporaryFile(string fileName) { if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName)) { return; } try { File.Delete(fileName); } catch { } } private void MoveStreamOutOfMemoryIfContentsLengthExceedThresholdAndSwitchingIsEnabled() { if (!this.stream.CanSeek) { return; } try { if ((this.stream.Length > this.thresholdLength) && !this.disableStreamSwitching) { this.MoveStreamContentsToFileStream(); } } catch (NotSupportedException) { } } private bool MoveStreamOutOfMemoryIfExpectedLengthExceedSwitchLength(long expectedLength) { if ((expectedLength >= this.thresholdLength) && !this.disableStreamSwitching) { this.MoveStreamContentsToFileStream(); return true; } return false; } private void MoveStreamContentsToFileStream() { var targetStream = CreateTemporaryFileStream(); this.isSafeToDisposeStream = true; if (this.stream.CanSeek && this.stream.Length == 0) { #if NETSTANDARD2_0 this.stream.Dispose(); #else this.stream.Close(); #endif this.stream = targetStream; return; } // Seek to 0 if we can, although if we can't seek, and we've already written (if the size is unknown) then // we are screwed anyway, and some streams that don't support seek also don't let you read the position so // there's no real way to check :-/ if (this.stream.CanSeek) { this.stream.Position = 0; } this.stream.CopyTo(targetStream, 8196); if (this.stream.CanWrite) { this.stream.Flush(); } this.stream = targetStream; this.disableStreamSwitching = true; } private static void ThrowExceptionIfCtorParametersWereInvalid(Stream stream, long expectedLength, long thresholdLength) { if (!stream.CanRead) { throw new InvalidOperationException("The stream must support reading."); } if (expectedLength < 0) { throw new ArgumentOutOfRangeException("expectedLength", expectedLength, "The value of the expectedLength parameter cannot be less than zero."); } if (thresholdLength < 0) { throw new ArgumentOutOfRangeException("thresholdLength", thresholdLength, "The value of the threshHoldLength parameter cannot be less than zero."); } } } } ================================================ FILE: src/Nancy/IO/UnclosableStreamWrapper.cs ================================================ namespace Nancy.IO { using System; using System.IO; /// /// To close the unclosable stream.. /// To fight the unbeatable foe.. /// To bear with unbearable sorrow.. /// To run where the brave dare not go.. /// public class UnclosableStreamWrapper : Stream, IDisposable { /// /// The wrapped stream /// private readonly Stream baseStream; /// /// Initializes a new instance of the class. /// /// The base stream to wrap. public UnclosableStreamWrapper(Stream baseStream) { if (baseStream == null) { throw new ArgumentNullException("baseStream"); } this.baseStream = baseStream; } /// /// Gets the base stream that the wrapper is wrapping /// public Stream BaseStream { get { return this.baseStream; } } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports reading. /// /// /// true if the stream supports reading; otherwise, false. /// /// 1 public override bool CanRead { get { return this.baseStream.CanRead; } } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. /// /// /// true if the stream supports seeking; otherwise, false. /// /// 1 public override bool CanSeek { get { return this.baseStream.CanSeek; } } /// /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. /// /// /// true if the stream supports writing; otherwise, false. /// /// 1 public override bool CanWrite { get { return this.baseStream.CanWrite; } } /// /// When overridden in a derived class, gets the length in bytes of the stream. /// /// /// A long value representing the length of the stream in bytes. /// /// A class derived from Stream does not support seeking. Methods were called after the stream was closed. 1 public override long Length { get { return this.baseStream.Length; } } /// /// When overridden in a derived class, gets or sets the position within the current stream. /// /// /// The current position within the stream. /// /// An I/O error occurs. The stream does not support seeking. Methods were called after the stream was closed. 1 public override long Position { get { return this.baseStream.Position; } set { this.baseStream.Position = value; } } /// /// Gets a value that determines whether the current stream can time out. /// /// /// A value that determines whether the current stream can time out. /// /// 2 public override bool CanTimeout { get { return this.baseStream.CanTimeout; } } /// /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to read before timing out. /// /// /// A value, in milliseconds, that determines how long the stream will attempt to read before timing out. /// /// The method always throws an . 2 public override int ReadTimeout { get { return this.baseStream.ReadTimeout; } set { this.baseStream.ReadTimeout = value; } } /// /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out. /// /// /// A value, in milliseconds, that determines how long the stream will attempt to write before timing out. /// /// The method always throws an . 2 public override int WriteTimeout { get { return this.baseStream.WriteTimeout; } set { this.baseStream.WriteTimeout = value; } } #if !NETSTANDARD1_6 /// /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. /// /// 1 public override void Close() { } #endif /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public new void Dispose() { } /// /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// /// An I/O error occurs. 2 public override void Flush() { this.baseStream.Flush(); } /// /// When overridden in a derived class, sets the position within the current stream. /// /// /// The new position within the current stream. /// /// A byte offset relative to the parameter. A value of type indicating the reference point used to obtain the new position. An I/O error occurs. The stream does not support seeking, such as if the stream is constructed from a pipe or console output. Methods were called after the stream was closed. 1 public override long Seek(long offset, SeekOrigin origin) { return this.baseStream.Seek(offset, origin); } /// /// When overridden in a derived class, sets the length of the current stream. /// /// The desired length of the current stream in bytes. An I/O error occurs. The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. Methods were called after the stream was closed. 2 public override void SetLength(long value) { this.baseStream.SetLength(value); } /// /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. /// /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. The zero-based byte offset in at which to begin storing the data read from the current stream. The maximum number of bytes to be read from the current stream. The sum of and is larger than the buffer length. is null. or is negative. An I/O error occurs. The stream does not support reading. Methods were called after the stream was closed. 1 public override int Read(byte[] buffer, int offset, int count) { return this.baseStream.Read(buffer, offset, count); } /// /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. /// /// An array of bytes. This method copies bytes from to the current stream. The zero-based byte offset in at which to begin copying bytes to the current stream. The number of bytes to be written to the current stream. The sum of and is greater than the buffer length. is null. or is negative. An I/O error occurs. The stream does not support writing. Methods were called after the stream was closed. 1 public override void Write(byte[] buffer, int offset, int count) { this.baseStream.Write(buffer, offset, count); } #if !NETSTANDARD1_6 /// /// Begins an asynchronous read operation. /// /// /// An that represents the asynchronous read, which could still be pending. /// /// The buffer to read the data into. The byte offset in at which to begin writing data read from the stream. The maximum number of bytes to read. An optional asynchronous callback, to be called when the read is complete. A user-provided object that distinguishes this particular asynchronous read request from other requests. Attempted an asynchronous read past the end of the stream, or a disk error occurs. One or more of the arguments is invalid. Methods were called after the stream was closed. The current Stream implementation does not support the read operation. 2 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.baseStream.BeginRead(buffer, offset, count, callback, state); } /// /// Begins an asynchronous write operation. /// /// /// An IAsyncResult that represents the asynchronous write, which could still be pending. /// /// The buffer to write data from. The byte offset in from which to begin writing. The maximum number of bytes to write. An optional asynchronous callback, to be called when the write is complete. A user-provided object that distinguishes this particular asynchronous write request from other requests. Attempted an asynchronous write past the end of the stream, or a disk error occurs. One or more of the arguments is invalid. Methods were called after the stream was closed. The current Stream implementation does not support the write operation. 2 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { return this.baseStream.BeginWrite(buffer, offset, count, callback, state); } /// /// Waits for the pending asynchronous read to complete. /// /// /// The number of bytes read from the stream, between zero (0) and the number of bytes you requested. Streams return zero (0) only at the end of the stream, otherwise, they should block until at least one byte is available. /// /// The reference to the pending asynchronous request to finish. is null. did not originate from a method on the current stream. The stream is closed or an internal error has occurred.2 public override int EndRead(IAsyncResult asyncResult) { return this.baseStream.EndRead(asyncResult); } /// /// Ends an asynchronous write operation. /// /// A reference to the outstanding asynchronous I/O request. is null. did not originate from a method on the current stream. The stream is closed or an internal error has occurred.2 public override void EndWrite(IAsyncResult asyncResult) { this.baseStream.EndWrite(asyncResult); } #endif /// /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. /// /// /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. /// /// The stream does not support reading. Methods were called after the stream was closed. 2 public override int ReadByte() { return this.baseStream.ReadByte(); } /// /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// /// The byte to write to the stream. An I/O error occurs. The stream does not support writing, or the stream is already closed. Methods were called after the stream was closed. 2 public override void WriteByte(byte value) { this.baseStream.WriteByte(value); } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { } } } ================================================ FILE: src/Nancy/IObjectSerializer.cs ================================================ namespace Nancy { /// /// De/Serialisation for cookie objects /// public interface IObjectSerializer { /// /// Serialize an object /// /// Source object /// Serialised object string string Serialize(object sourceObject); /// /// Deserialize an object string /// /// Source object string /// Deserialized object object Deserialize(string sourceString); } } ================================================ FILE: src/Nancy/IObjectSerializerSelector.cs ================================================ namespace Nancy { /// /// Allows setting of the serializer for session object storage /// public interface IObjectSerializerSelector : IHideObjectMembers { /// /// Using the specified serializer /// /// Serializer to use void WithSerializer(IObjectSerializer newSerializer); } } ================================================ FILE: src/Nancy/IResourceAssemblyProvider.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.Reflection; /// /// Defines the functionality for retrieving which assemblies that should be used by Nancy. /// public interface IResourceAssemblyProvider { /// /// Gets a list of assemblies that should be scanned. /// /// An of instances. IEnumerable GetAssembliesToScan(); } } ================================================ FILE: src/Nancy/IResponseFormatter.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// An extension point for adding support for formatting response contents. No members should be added to this interface without good reason. /// /// Extension methods to this interface should always return or one of the types that can implicitly be types into a . public interface IResponseFormatter : IHideObjectMembers { /// /// Gets all factory. /// ISerializerFactory SerializerFactory { get; } /// /// Gets the context for which the response is being formatted. /// /// A instance. NancyContext Context { get; } /// /// Gets the . /// /// An instance. INancyEnvironment Environment { get; } /// /// Gets the root path of the application. /// /// A containing the root path. string RootPath { get; } } } ================================================ FILE: src/Nancy/IResponseFormatterFactory.cs ================================================ namespace Nancy { /// /// Defines the functionality of a factory. /// public interface IResponseFormatterFactory { /// /// Creates a new instance. /// /// The instance that should be used by the response formatter. /// An instance. IResponseFormatter Create(NancyContext context); } } ================================================ FILE: src/Nancy/IRootPathProvider.cs ================================================ namespace Nancy { /// /// Defines the functionality to retrieve the root folder path of the current Nancy application. /// public interface IRootPathProvider : IHideObjectMembers { /// /// Returns the root folder path of the current Nancy application. /// /// A containing the path of the root folder. string GetRootPath(); } } ================================================ FILE: src/Nancy/IRuntimeEnvironmentInformation.cs ================================================ namespace Nancy { /// /// Defines functionality for getting information about the runtime execution environment. /// public interface IRuntimeEnvironmentInformation { /// /// Gets a value indicating if the application is running in debug mode. /// /// if the application is running in debug mode, otherwise . bool IsDebug { get; } } } ================================================ FILE: src/Nancy/ISerializer.cs ================================================ namespace Nancy { using System.Collections.Generic; using System.IO; using Nancy.Responses.Negotiation; /// /// Defines the functionality for providing serialization support. /// public interface ISerializer { /// /// Whether the serializer can serialize the content type /// /// Content type to serialise /// True if supported, false otherwise bool CanSerialize(MediaRange mediaRange); /// /// Gets the list of extensions that the serializer can handle. /// /// An of extensions if any are available, otherwise an empty enumerable. IEnumerable Extensions { get; } /// /// Serialize the given model with the given contentType /// /// Content type to serialize into /// Model to serialize /// Output stream to serialize to /// Serialised object void Serialize(MediaRange mediaRange, TModel model, Stream outputStream); } } ================================================ FILE: src/Nancy/ISerializerFactory.cs ================================================ namespace Nancy { using Nancy.Responses.Negotiation; /// /// Defines the functionality of an factory. /// public interface ISerializerFactory { /// /// Gets the implementation that can serialize the provided . /// /// The to get a serializer for. /// An instance, or if not match was found. ISerializer GetSerializer(MediaRange mediaRange); } } ================================================ FILE: src/Nancy/IStaticContentProvider.cs ================================================ namespace Nancy { /// /// Provides static content delivery /// public interface IStaticContentProvider { /// /// Gets the static content response, if possible. /// /// Current context /// Response if serving content, null otherwise Response GetContent(NancyContext context); } } ================================================ FILE: src/Nancy/IStatusCodeHandler.cs ================================================ namespace Nancy.ErrorHandling { /// /// Provides informative responses for particular HTTP status codes /// public interface IStatusCodeHandler { /// /// Check if the error handler can handle errors of the provided status code. /// /// Status code /// The instance of the current request. /// True if handled, false otherwise bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context); /// /// Handle the error code /// /// Status code /// Current context void Handle(HttpStatusCode statusCode, NancyContext context); } } ================================================ FILE: src/Nancy/ITypeCatalog.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; /// /// Defines the functionality of a type catalog. /// public interface ITypeCatalog { /// /// Gets all types that are assignable to the provided . /// /// The that returned types should be assignable to. /// A that should be used when retrieving types. /// An of instances. IReadOnlyCollection GetTypesAssignableTo(Type type, TypeResolveStrategy strategy); } } ================================================ FILE: src/Nancy/IncludeInNancyAssemblyScanningAttribute.cs ================================================ namespace Nancy { using System; /// /// Add this attribute to an assembly to make sure /// it is included in Nancy's assembly scanning. /// /// /// Apply the attribute, typically in AssemblyInfo.(cs|fs|vb), as follows: /// [assembly: IncludeInNancyAssemblyScanning] /// [AttributeUsage(AttributeTargets.Assembly)] public sealed class IncludeInNancyAssemblyScanningAttribute : Attribute { } } ================================================ FILE: src/Nancy/Json/Converters/TimeSpanConverter.cs ================================================ namespace Nancy.Json.Converters { using System; using System.Collections.Generic; /// /// Converts a dictionary with time info into a time span instance or vice versa. /// /// public class TimeSpanConverter : JavaScriptConverter { /// /// Gets the supported types. /// /// The collection of supported types. public override IEnumerable SupportedTypes { get { return new[] { typeof(TimeSpan) }; } } /// /// Deserializes the specified dictionary into a timespan instance. /// /// The dictionary. /// The type. /// The serializer. /// A instance as public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) { return new TimeSpan( this.GetValue(dictionary, "Days"), this.GetValue(dictionary, "Hours"), this.GetValue(dictionary, "Minutes"), this.GetValue(dictionary, "Seconds"), this.GetValue(dictionary, "Milliseconds")); } /// /// Serializes the specified object. /// /// The object. /// The serializer. /// A representing a object public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) { var timeSpan = (TimeSpan)obj; var result = new Dictionary { { "Days", timeSpan.Days }, { "Hours", timeSpan.Hours }, { "Minutes", timeSpan.Minutes }, { "Seconds", timeSpan.Seconds }, { "Milliseconds", timeSpan.Milliseconds } }; return result; } private int GetValue(IDictionary dictionary, string key) { const int DefaultValue = 0; object value; if (!dictionary.TryGetValue(key, out value)) { return DefaultValue; } if (value is int) { return (int)value; } if (value is long) { return Convert.ToInt32((long)value); } var valueString = value as string; if (valueString == null) { return DefaultValue; } int returnValue; return !int.TryParse(valueString, out returnValue) ? DefaultValue : returnValue; } } } ================================================ FILE: src/Nancy/Json/Converters/TupleConverter.cs ================================================ namespace Nancy.Json.Converters { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; /// /// Converts a dictionary into a list of tuples. /// /// public class TupleConverter : JavaScriptConverter { /// /// Gets the supported tuple types. /// /// The supported types. public override IEnumerable SupportedTypes { get { yield return typeof(Tuple<>); yield return typeof(Tuple<,>); yield return typeof(Tuple<,,>); yield return typeof(Tuple<,,,>); yield return typeof(Tuple<,,,,>); yield return typeof(Tuple<,,,,,>); yield return typeof(Tuple<,,,,,,>); yield return typeof(Tuple<,,,,,,,>); } } /// /// Deserializes the specified dictionary into a list of tuples. /// /// The dictionary. /// The type. /// The serializer. /// A list of representing the public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer) { var ctor = type.GetConstructors().First(); object instance = ctor.Invoke(dictionary.Values.ToArray()); return instance; } /// /// Serializes the specified object. /// /// The object. /// The serializer. /// public override IDictionary Serialize(object obj, JavaScriptSerializer serializer) { throw new NotImplementedException(); } } } ================================================ FILE: src/Nancy/Json/DefaultJsonConfigurationProvider.cs ================================================ namespace Nancy.Json { using Nancy.Configuration; /// /// Provides the default configuration for . /// public class DefaultJsonConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance /// Will return public override JsonConfiguration GetDefaultConfiguration() { return JsonConfiguration.Default; } } } ================================================ FILE: src/Nancy/Json/JavaScriptConverter.cs ================================================ // // JavaScriptConverter.cs // // Author: // Igor Zelmanovich // // (C) 2007 Mainsoft, Inc. http://www.mainsoft.com // // // 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. // namespace Nancy.Json { using System; using System.Collections.Generic; /// /// Abstracr base class for javascript converter operations. /// public abstract class JavaScriptConverter { /// /// Initializes a new instance of the class. /// protected JavaScriptConverter () { } /// /// Gets the supported types. /// /// The supported types. public abstract IEnumerable SupportedTypes { get; } /// /// Deserializes the specified dictionary. /// /// The dictionary. /// The type. /// The deserialized public virtual object Deserialize(IDictionary dictionary, Type type) { return Deserialize(dictionary, type, null); } /// /// Deserializes the specified dictionary. /// /// The dictionary. /// The type. /// The serializer. /// An representing public abstract object Deserialize (IDictionary dictionary, Type type, JavaScriptSerializer serializer); /// /// Serializes the specified object. /// /// The object. /// A instance public IDictionary Serialize(object obj) { return Serialize(obj, null); } /// /// Serializes the specified object. /// /// The object. /// The serializer. /// A instance public abstract IDictionary Serialize (object obj, JavaScriptSerializer serializer); } } ================================================ FILE: src/Nancy/Json/JavaScriptPrimitiveConverter.cs ================================================ namespace Nancy.Json { using System; using System.Collections.Generic; /// /// Operations for converting javascript primitives. /// public abstract class JavaScriptPrimitiveConverter { /// /// Gets the supported types. /// /// The supported types. public abstract IEnumerable SupportedTypes { get; } /// /// Deserializes the specified primitive value. /// /// The primitive value. /// The type. /// The deserialized public virtual object Deserialize(object primitiveValue, Type type) { return Deserialize(primitiveValue, type, null); } /// /// Deserializes the specified primitive value. /// /// The primitive value. /// The type. /// The serializer. /// The deserialized public abstract object Deserialize(object primitiveValue, Type type, JavaScriptSerializer serializer); /// /// Serializes the specified object. /// /// The object. /// The serialized public virtual object Serialize(object obj) { return Serialize(obj, null); } /// /// Serializes the specified object. /// /// The object. /// The serializer. /// The serialized public abstract object Serialize(object obj, JavaScriptSerializer serializer); } } ================================================ FILE: src/Nancy/Json/JavaScriptSerializer.cs ================================================ // // JavaScriptSerializer.cs // // Author: // Konstantin Triger // // (C) 2007 Mainsoft, Inc. http://www.mainsoft.com // // // 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. // namespace Nancy.Json { using System; using System.Collections.Generic; using System.IO; using Nancy.Json.Simple; using Nancy.Extensions; /// /// JavaScriptSerializer responsible for serializing objects /// public class JavaScriptSerializer { private readonly JsonConfiguration jsonConfiguration; private readonly GlobalizationConfiguration globalizationConfiguration; private readonly NancySerializationStrategy serializerStrategy; /// /// Creates an instance of /// public JavaScriptSerializer() : this(JsonConfiguration.Default, GlobalizationConfiguration.Default) { } /// /// Creates an instance of /// /// A object to configure the serializer /// A object to configure the serializer public JavaScriptSerializer(JsonConfiguration jsonConfiguration, GlobalizationConfiguration globalizationConfiguration) { this.jsonConfiguration = jsonConfiguration; this.globalizationConfiguration = globalizationConfiguration; this.serializerStrategy = new NancySerializationStrategy(jsonConfiguration.RetainCasing, jsonConfiguration.SerializeEnumToString); } /// /// Creates an instance of /// /// A object to configure the serializer /// A boolean to determine whether to register custom converters /// A object to configure the serializer public JavaScriptSerializer(JsonConfiguration jsonConfiguration, bool registerConverters, GlobalizationConfiguration globalizationConfiguration) : this(jsonConfiguration, globalizationConfiguration) { this.jsonConfiguration = jsonConfiguration; this.globalizationConfiguration = globalizationConfiguration; if (registerConverters) { this.RegisterConverters(jsonConfiguration.Converters, jsonConfiguration.PrimitiveConverters); } } /// /// Deserialize JSON /// /// JSON representation /// The to deserialize into /// An instance of type representing as an object public T Deserialize(string input) { return SimpleJson.DeserializeObject(input, this.serializerStrategy, this.globalizationConfiguration.DateTimeStyles); } /// /// Deserialize JSON /// /// JSON representation /// An object representing public object DeserializeObject(string input) { return SimpleJson.DeserializeObject(input, null, this.serializerStrategy, this.globalizationConfiguration.DateTimeStyles); } /// /// Register custom JSON converters /// /// An array of to register /// is null public void RegisterConverters(IEnumerable converters) { if (converters == null) { throw new ArgumentNullException("converters"); } this.serializerStrategy.RegisterConverters(converters); } /// /// Register custom JSON converters /// /// An array of /// is null public void RegisterConverters(IEnumerable primitiveConverters) { if (primitiveConverters == null) { throw new ArgumentNullException("primitiveConverters"); } this.serializerStrategy.RegisterConverters(primitiveConverters); } /// /// Register custom JSON converters /// /// An array of to register /// An array of public void RegisterConverters(IEnumerable converters, IEnumerable primitiveConverters) { if (converters != null) { this.RegisterConverters(converters); } if (primitiveConverters != null) { this.RegisterConverters(primitiveConverters); } } /// /// Serialize an object to JSON /// /// The object to serialize /// A JSON string representation of public string Serialize(object obj) { return SimpleJson.SerializeObject(obj, this.serializerStrategy, this.jsonConfiguration.ExcludeNullValues); } /// /// Serialize an object to JSON and write result to /// /// The object to serialize /// An instance of to write the serialized public void Serialize(object obj, TextWriter output) { output.Write(this.Serialize(obj)); } } } ================================================ FILE: src/Nancy/Json/Json.cs ================================================ // // Json.cs // // Author: // Marek Habersack // // (C) 2008 Novell, Inc. http://novell.com/ // // // 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. // namespace Nancy.Json { using System; /// /// JSON Helper Class. /// public static class Json { private const StringComparison ComparisonType = StringComparison.OrdinalIgnoreCase; /// /// Attempts to detect if the content type is JSON. /// Supports: /// application/json /// text/json /// [something]+json /// Matches are case insensitive to try and be as "accepting" as possible. /// /// Request content type /// True if content type is JSON, false otherwise public static bool IsJsonContentType(string contentType) { if (string.IsNullOrEmpty(contentType)) { return false; } var index = contentType.IndexOf(';'); if (index >= 0) { contentType = contentType.Substring(0, index); } contentType = contentType.Trim(); return contentType.StartsWith("application/json", ComparisonType) || contentType.Equals("text/json", ComparisonType) || contentType.EndsWith("+json", ComparisonType); } } } ================================================ FILE: src/Nancy/Json/JsonConfiguration.cs ================================================ namespace Nancy.Json { using System.Collections.Generic; using System.Text; using Nancy.Json.Converters; /// /// Configuration for JSON serialization. /// public class JsonConfiguration { /// /// A default instance of the class. /// public static readonly JsonConfiguration Default = new JsonConfiguration { Converters = new List { new TimeSpanConverter(), new TupleConverter() }, DefaultEncoding = Encoding.UTF8, PrimitiveConverters = new List(), RetainCasing = false, SerializeEnumToString = false, ExcludeNullValues = false }; private JsonConfiguration() { } /// /// Initializes a new instance of the class. /// /// The default that should be used by the serializer. /// List of instances. /// List of instances. /// if the name casing should be retained during serialization, otherwise . /// if enums should be represented as string otherwise . /// if the serializer should exclude null values for properties on objects otherwise . public JsonConfiguration(Encoding defaultEncoding, IList converters, IList primitiveConverters, bool? retainCasing, bool? serializeEnumToString, bool? excludeNullValues) { this.DefaultEncoding = defaultEncoding ?? Default.DefaultEncoding; this.Converters = converters ?? Default.Converters; this.PrimitiveConverters = primitiveConverters ?? Default.PrimitiveConverters; this.RetainCasing = retainCasing ?? Default.RetainCasing; this.SerializeEnumToString = serializeEnumToString ?? Default.SerializeEnumToString; this.ExcludeNullValues = excludeNullValues ?? Default.ExcludeNullValues; } /// /// Gets the default for JSON responses. /// /// The default is . public Encoding DefaultEncoding { get; private set; } /// /// Gets or sets the type converters that should be used. /// /// The default is and . public IList Converters { get; private set; } /// /// Gets or sets the converters used for primitive types. /// /// The default are no converters. public IList PrimitiveConverters { get; private set; } /// /// Gets or sets if C# casing should be retained or if camel-casing should be enforeced. /// /// The default is . public bool RetainCasing { get; private set; } /// /// Get or sets whether enums should be treated as string /// public bool SerializeEnumToString{ get; private set; } /// /// Gets or sets if the serializer should return null values for properties on objects /// public bool ExcludeNullValues { get; set; } } } ================================================ FILE: src/Nancy/Json/JsonConfigurationExtensions.cs ================================================ namespace Nancy.Json { using System.Collections.Generic; using System.Text; using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class JsonConfigurationExtensions { /// /// Configures JSON serialization. /// /// that should be configured. /// The that should be as a default. /// List of that should be used. /// List of that should be used. /// if C# casing should be retained, otherwise to use camel-casing. /// if enums should be represented as string otherwise . /// if the serializer should exclude null values for properties on objects otherwise . public static void Json(this INancyEnvironment environment, Encoding defaultEncoding = null, IList converters = null, IList primitiveConverters = null, bool? retainCasing = null, bool? serializeEnumToString = null, bool? excludeNullValues = false) { environment.AddValue(new JsonConfiguration( defaultEncoding ?? JsonConfiguration.Default.DefaultEncoding, converters ?? JsonConfiguration.Default.Converters, primitiveConverters ?? JsonConfiguration.Default.PrimitiveConverters, retainCasing ?? JsonConfiguration.Default.RetainCasing, serializeEnumToString ?? JsonConfiguration.Default.SerializeEnumToString, excludeNullValues ?? JsonConfiguration.Default.ExcludeNullValues)); } } } ================================================ FILE: src/Nancy/Json/ScriptIgnoreAttribute.cs ================================================ // // ScriptIgnoreAttribute.cs // // Author: // Igor Zelmanovich // // (C) 2007 Mainsoft, Inc. http://www.mainsoft.com // // // 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. // namespace Nancy.Json { using System; /// /// Property attribute for ignoring scripts. /// /// [AttributeUsage (AttributeTargets.Property | AttributeTargets.Field)] public sealed class ScriptIgnoreAttribute : Attribute { } } ================================================ FILE: src/Nancy/Json/Simple/NancySerializationStrategy.cs ================================================ namespace Nancy.Json.Simple { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Nancy serialization stategy for SimpleJson /// public class NancySerializationStrategy : PocoJsonSerializerStrategy { private readonly bool retainCasing; private readonly bool serializeEnumToString; private readonly List converters = new List(); private readonly List primitiveConverters = new List(); private readonly ConcurrentDictionary converterCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary primitiveConverterCache = new ConcurrentDictionary(); /// /// Initializes a new instance of the class. /// /// C# casing of objects will be defaulted to camelCase and enums treated as integers public NancySerializationStrategy() : this(false, false) { } /// /// Initializes a new instance of the class. /// /// Retain C# casing of objects when serialized /// Should enums be represented as string public NancySerializationStrategy(bool retainCasing, bool serializeEnumToString) { this.retainCasing = retainCasing; this.serializeEnumToString = serializeEnumToString; } /// /// Register custom converters /// /// An array of public void RegisterConverters(IEnumerable javaScriptConverters) { this.converters.AddRange(javaScriptConverters); } /// /// Register custom /// /// An array of public void RegisterConverters(IEnumerable javaScriptPrimitiveConverters) { this.primitiveConverters.AddRange(javaScriptPrimitiveConverters); } /// /// Formats a property name to a JSON field name /// /// The property name to format /// camelCase if retainCasing is false, otherwise protected override string MapClrMemberNameToJsonFieldName(string clrPropertyName) { return this.retainCasing ? base.MapClrMemberNameToJsonFieldName(clrPropertyName) : clrPropertyName.ToCamelCase(); } /// /// Deserialize an object /// /// The object to deserialize /// The type of object to deserialize /// The ton convert objects /// A instance of deserialized from public override object DeserializeObject(object value, Type type, DateTimeStyles dateTimeStyles) { var typeInfo = type.GetTypeInfo(); if (typeInfo.IsEnum || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type).GetTypeInfo().IsEnum)) { var typeToParse = ReflectionUtils.IsNullableType(type) ? Nullable.GetUnderlyingType(type) : type; return value == null ? null : Enum.Parse(typeToParse, value.ToString(), true); } var primitiveConverter = this.FindPrimitiveConverter(type); if (primitiveConverter != null) { return primitiveConverter.Deserialize(value, type); } var valueDictionary = value as IDictionary; if (valueDictionary == null) { return base.DeserializeObject(value, type, dateTimeStyles); } var javascriptConverter = this.FindJavaScriptConverter(type); if (javascriptConverter != null) { return javascriptConverter.Deserialize(valueDictionary, type); } if (!typeInfo.IsGenericType) { return base.DeserializeObject(value, type, dateTimeStyles); } var genericType = typeInfo.GetGenericTypeDefinition(); var genericTypeConverter = this.FindJavaScriptConverter(genericType); if (genericTypeConverter == null) { return base.DeserializeObject(value, type, dateTimeStyles); } var values = new Dictionary(StringComparer.OrdinalIgnoreCase); var genericArguments = type.GetGenericArguments(); for (var i = 0; i < genericArguments.Length; i++) { var deserializedObject = this.DeserializeObject(valueDictionary.Values.ElementAt(i), genericArguments[i], dateTimeStyles); values.Add(valueDictionary.Keys.ElementAt(i), deserializedObject); } return genericTypeConverter.Deserialize(values, type); } /// /// Serializes an . /// /// The enum value to serialize. /// The serialized value. protected override object SerializeEnum(Enum p) { if (this.serializeEnumToString) { return p.ToString(); } return base.SerializeEnum(p); } /// /// Serialize an object /// /// The object to serialize /// The serialized object /// true if was converted successfully; otherwise, false protected override bool TrySerializeKnownTypes(object input, out object output) { var dynamicValue = input as DynamicDictionaryValue; if (!ReferenceEquals(dynamicValue, null) && dynamicValue.HasValue) { output = dynamicValue.Value; return true; } var inputType = input.GetType(); if (this.TrySerializeJavaScriptConverter(input, out output, inputType)) { return true; } if (this.TrySerializePrimitiveConverter(input, ref output, inputType)) { return true; } var type = input as Type; if (type != null) { output = type.GetTypeInfo().FullName; return true; } if (input is DateTime) { return this.SerializeDateTime((DateTime)input, out output); } if (input is DateTimeOffset) { var dto = (DateTimeOffset)input; output = dto.ToString("o", CultureInfo.InvariantCulture); return true; } return base.TrySerializeKnownTypes(input, out output); } private bool SerializeDateTime(DateTime input, out object output) { var dateTime = input; if (dateTime.Kind == DateTimeKind.Unspecified) { dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Local); } output = dateTime.ToString("o", CultureInfo.InvariantCulture); return true; } private bool TrySerializePrimitiveConverter(object input, ref object output, Type inputType) { var primitiveConverter = this.FindPrimitiveConverter(inputType); if (primitiveConverter == null) { return false; } output = primitiveConverter.Serialize(input); return true; } private JavaScriptPrimitiveConverter FindPrimitiveConverter(Type inputType) { return this.primitiveConverterCache.GetOrAdd(inputType, typeToConvert => this.primitiveConverters.FirstOrDefault(converter => converter.SupportedTypes.Any(supportedType => supportedType.IsAssignableFrom(typeToConvert)))); } private bool TrySerializeJavaScriptConverter(object input, out object output, Type inputType) { output = null; var converter = this.FindJavaScriptConverter(inputType); if (converter == null) { return false; } var result = converter.Serialize(input); output = result.ToDictionary(kvp => this.MapClrMemberNameToJsonFieldName(kvp.Key), kvp => kvp.Value); return true; } private JavaScriptConverter FindJavaScriptConverter(Type inputType) { return this.converterCache.GetOrAdd(inputType, typeToConvert => this.converters.FirstOrDefault(converter => converter.SupportedTypes.Any(supportedType => supportedType.IsAssignableFrom(typeToConvert)))); } } } ================================================ FILE: src/Nancy/Json/Simple/SimpleJson.cs ================================================ #pragma warning disable CS1591, CS1574, CS1711, CS1712 // Disable XML comment related warnings //----------------------------------------------------------------------- // // Copyright (c) 2011, The Outercurve Foundation. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.opensource.org/licenses/mit-license.php // // 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. // // Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- // VERSION: 0.38.0 // NOTE: uncomment the following line to make SimpleJson class internal. //#define SIMPLE_JSON_INTERNAL // NOTE: uncomment the following line to make JsonArray and JsonObject class internal. //#define SIMPLE_JSON_OBJARRAYINTERNAL // NOTE: uncomment the following line to enable dynamic support. #define SIMPLE_JSON_DYNAMIC // NOTE: uncomment the following line to enable DataContract support. //#define SIMPLE_JSON_DATACONTRACT // NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. //#define SIMPLE_JSON_READONLY_COLLECTIONS // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION // NOTE: uncomment the following line if you are compiling under Window Metro style application/library. // usually already defined in properties //#define NETFX_CORE; // If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; #define SIMPLE_JSON_TYPEINFO // original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html #if NETFX_CORE #define SIMPLE_JSON_TYPEINFO #endif namespace Nancy.Json.Simple { using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.Serialization; using System.Text; // ReSharper disable LoopCanBeConvertedToQuery // ReSharper disable RedundantExplicitArrayCreation // ReSharper disable SuggestUseVarKeywordEvident /// /// Represents the json array. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonArray : List { /// /// Initializes a new instance of the class. /// public JsonArray() { } /// /// Initializes a new instance of the class. /// /// The capacity of the json array. public JsonArray(int capacity) : base(capacity) { } /// /// The json representation of the array. /// /// The json representation of the array. public override string ToString() { return SimpleJson.SerializeObject(this) ?? string.Empty; } } /// /// Represents the json object. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonObject : #if SIMPLE_JSON_DYNAMIC DynamicObject, #endif IDictionary { /// /// The internal member dictionary. /// private readonly Dictionary _members; /// /// Initializes a new instance of . /// public JsonObject() : this(StringComparer.OrdinalIgnoreCase) { } /// /// Initializes a new instance of . /// /// The implementation to use when comparing keys, or null to use the default for the type of the key. public JsonObject(IEqualityComparer comparer) { this._members = new Dictionary(comparer); } /// /// Gets the at the specified index. /// /// public object this[int index] { get { return GetAtIndex(this._members, index); } } internal static object GetAtIndex(IDictionary obj, int index) { if (obj == null) throw new ArgumentNullException("obj"); if (index >= obj.Count) throw new ArgumentOutOfRangeException("index"); int i = 0; foreach (KeyValuePair o in obj) if (i++ == index) return o.Value; return null; } /// /// Adds the specified key. /// /// The key. /// The value. public void Add(string key, object value) { this._members.Add(key, value); } /// /// Determines whether the specified key contains key. /// /// The key. /// /// true if the specified key contains key; otherwise, false. /// public bool ContainsKey(string key) { return this._members.ContainsKey(key); } /// /// Gets the keys. /// /// The keys. public ICollection Keys { get { return this._members.Keys; } } /// /// Removes the specified key. /// /// The key. /// public bool Remove(string key) { return this._members.Remove(key); } /// /// Tries the get value. /// /// The key. /// The value. /// public bool TryGetValue(string key, out object value) { return this._members.TryGetValue(key, out value); } /// /// Gets the values. /// /// The values. public ICollection Values { get { return this._members.Values; } } /// /// Gets or sets the with the specified key. /// /// public object this[string key] { get { return this._members[key]; } set { this._members[key] = value; } } /// /// Adds the specified item. /// /// The item. public void Add(KeyValuePair item) { this._members.Add(item.Key, item.Value); } /// /// Clears this instance. /// public void Clear() { this._members.Clear(); } /// /// Determines whether [contains] [the specified item]. /// /// The item. /// /// true if [contains] [the specified item]; otherwise, false. /// public bool Contains(KeyValuePair item) { object value; return this._members.TryGetValue(item.Key, out value) && value == item.Value; } /// /// Copies to. /// /// The array. /// Index of the array. public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); int num = this.Count; foreach (KeyValuePair kvp in this) { array[arrayIndex++] = kvp; if (--num <= 0) return; } } /// /// Gets the count. /// /// The count. public int Count { get { return this._members.Count; } } /// /// Gets a value indicating whether this instance is read only. /// /// /// true if this instance is read only; otherwise, false. /// public bool IsReadOnly { get { return false; } } /// /// Removes the specified item. /// /// The item. /// public bool Remove(KeyValuePair item) { return this._members.Remove(item.Key); } /// /// Gets the enumerator. /// /// public IEnumerator> GetEnumerator() { return this._members.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return this._members.GetEnumerator(); } /// /// Returns a json that represents the current . /// /// /// A json that represents the current . /// public override string ToString() { return SimpleJson.SerializeObject(this); } #if SIMPLE_JSON_DYNAMIC /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. /// The result of the type conversion operation. /// /// Alwasy returns true. /// public override bool TryConvert(ConvertBinder binder, out object result) { // if (binder == null) throw new ArgumentNullException("binder"); // Type targetType = binder.Type; if ((targetType == typeof(IEnumerable)) || (targetType == typeof(IEnumerable>)) || (targetType == typeof(IDictionary)) || (targetType == typeof(IDictionary))) { result = this; return true; } return base.TryConvert(binder, out result); } /// /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. /// /// Provides information about the deletion. /// /// Alwasy returns true. /// public override bool TryDeleteMember(DeleteMemberBinder binder) { // if (binder == null) throw new ArgumentNullException("binder"); // return this._members.Remove(binder.Name); } /// /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. /// The result of the index operation. /// /// Alwasy returns true. /// public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { result = ((IDictionary)this)[(string)indexes[0]]; return true; } result = null; return true; } /// /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . /// /// Alwasy returns true. /// public override bool TryGetMember(GetMemberBinder binder, out object result) { object value; if (this._members.TryGetValue(binder.Name, out value)) { result = value; return true; } result = null; return true; } /// /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. /// public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { ((IDictionary)this)[(string)indexes[0]] = value; return true; } return base.TrySetIndex(binder, indexes, value); } /// /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// public override bool TrySetMember(SetMemberBinder binder, object value) { // if (binder == null) throw new ArgumentNullException("binder"); // this._members[binder.Name] = value; return true; } /// /// Returns the enumeration of all dynamic member names. /// /// /// A sequence that contains dynamic member names. /// public override IEnumerable GetDynamicMemberNames() { foreach (var key in this.Keys) yield return key; } #endif } /// /// This class encodes and decodes JSON strings. /// Spec. details, see http://www.json.org/ /// /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). /// All numbers are parsed to doubles. /// [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif static class SimpleJson { private const int TOKEN_NONE = 0; private const int TOKEN_CURLY_OPEN = 1; private const int TOKEN_CURLY_CLOSE = 2; private const int TOKEN_SQUARED_OPEN = 3; private const int TOKEN_SQUARED_CLOSE = 4; private const int TOKEN_COLON = 5; private const int TOKEN_COMMA = 6; private const int TOKEN_STRING = 7; private const int TOKEN_NUMBER = 8; private const int TOKEN_TRUE = 9; private const int TOKEN_FALSE = 10; private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; private static readonly char[] EscapeTable; private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; private static readonly string EscapeCharactersString = new string(EscapeCharacters); static SimpleJson() { EscapeTable = new char[93]; EscapeTable['"'] = '"'; EscapeTable['\\'] = '\\'; EscapeTable['\b'] = 'b'; EscapeTable['\f'] = 'f'; EscapeTable['\n'] = 'n'; EscapeTable['\r'] = 'r'; EscapeTable['\t'] = 't'; } /// /// Parses the string json into a value /// /// A JSON string. /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false public static object DeserializeObject(string json) { if (string.IsNullOrWhiteSpace(json)) { return null; } object obj; if (TryDeserializeObject(json, out obj)) return obj; throw new SerializationException("Invalid JSON string"); } /// /// Try parsing the json string into a value. /// /// /// A JSON string. /// /// /// The object. /// /// /// Returns true if successfull otherwise false. /// [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] public static bool TryDeserializeObject(string json, out object obj) { bool success = true; if (json != null) { char[] charArray = json.ToCharArray(); int index = 0; obj = ParseValue(charArray, ref index, ref success); } else obj = null; return success; } public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy, DateTimeStyles dateTimeStyles) { object jsonObject = DeserializeObject(json); return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) ? jsonObject : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type, dateTimeStyles); } public static object DeserializeObject(string json, Type type, DateTimeStyles dateTimeStyles) { return DeserializeObject(json, type, null, dateTimeStyles); } public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy, DateTimeStyles dateTimeStyles) { return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy, dateTimeStyles); } public static T DeserializeObject(string json) { return (T)DeserializeObject(json, typeof(T), null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); } /// /// Converts a IDictionary<string,object> / IList<object> object into a JSON string /// /// A IDictionary<string,object> / IList<object> /// Serializer strategy to use /// if the serializer should exclude null values for properties on objects otherwise /// A JSON encoded string, or null if object 'json' is not serializable public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy, bool excludeNullValues) { StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); bool success = SerializeValue(jsonSerializerStrategy, json, builder, excludeNullValues); return (success ? builder.ToString() : null); } public static string SerializeObject(object json) { return SerializeObject(json, CurrentJsonSerializerStrategy, false); } public static string EscapeToJavascriptString(string jsonString) { if (string.IsNullOrEmpty(jsonString)) return jsonString; StringBuilder sb = new StringBuilder(); char c; for (int i = 0; i < jsonString.Length; ) { c = jsonString[i++]; if (c == '\\') { int remainingLength = jsonString.Length - i; if (remainingLength >= 2) { char lookahead = jsonString[i]; if (lookahead == '\\') { sb.Append('\\'); ++i; } else if (lookahead == '"') { sb.Append("\""); ++i; } else if (lookahead == 't') { sb.Append('\t'); ++i; } else if (lookahead == 'b') { sb.Append('\b'); ++i; } else if (lookahead == 'n') { sb.Append('\n'); ++i; } else if (lookahead == 'r') { sb.Append('\r'); ++i; } } } else { sb.Append(c); } } return sb.ToString(); } static IDictionary ParseObject(char[] json, ref int index, ref bool success) { IDictionary table = new JsonObject(); int token; // { NextToken(json, ref index); bool done = false; while (!done) { token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_CURLY_CLOSE) { NextToken(json, ref index); return table; } else { // name string name = ParseString(json, ref index, ref success); if (!success) { success = false; return null; } // : token = NextToken(json, ref index); if (token != TOKEN_COLON) { success = false; return null; } // value object value = ParseValue(json, ref index, ref success); if (!success) { success = false; return null; } table[name] = value; } } return table; } static JsonArray ParseArray(char[] json, ref int index, ref bool success) { JsonArray array = new JsonArray(); // [ NextToken(json, ref index); bool done = false; while (!done) { int token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_SQUARED_CLOSE) { NextToken(json, ref index); break; } else { object value = ParseValue(json, ref index, ref success); if (!success) return null; array.Add(value); } } return array; } static object ParseValue(char[] json, ref int index, ref bool success) { switch (LookAhead(json, index)) { case TOKEN_STRING: return ParseString(json, ref index, ref success); case TOKEN_NUMBER: return ParseNumber(json, ref index, ref success); case TOKEN_CURLY_OPEN: return ParseObject(json, ref index, ref success); case TOKEN_SQUARED_OPEN: return ParseArray(json, ref index, ref success); case TOKEN_TRUE: NextToken(json, ref index); return true; case TOKEN_FALSE: NextToken(json, ref index); return false; case TOKEN_NULL: NextToken(json, ref index); return null; case TOKEN_NONE: break; } success = false; return null; } static string ParseString(char[] json, ref int index, ref bool success) { StringBuilder s = new StringBuilder(BUILDER_CAPACITY); char c; EatWhitespace(json, ref index); // " c = json[index++]; bool complete = false; while (!complete) { if (index == json.Length) break; c = json[index++]; if (c == '"') { complete = true; break; } else if (c == '\\') { if (index == json.Length) break; c = json[index++]; if (c == '"') s.Append('"'); else if (c == '\\') s.Append('\\'); else if (c == '/') s.Append('/'); else if (c == 'b') s.Append('\b'); else if (c == 'f') s.Append('\f'); else if (c == 'n') s.Append('\n'); else if (c == 'r') s.Append('\r'); else if (c == 't') s.Append('\t'); else if (c == 'u') { int remainingLength = json.Length - index; if (remainingLength >= 4) { // parse the 32 bit hex into an integer codepoint uint codePoint; if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) return ""; // convert the integer codepoint to a unicode char and add to string if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate { index += 4; // skip 4 chars remainingLength = json.Length - index; if (remainingLength >= 6) { uint lowCodePoint; if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) { if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate { s.Append((char)codePoint); s.Append((char)lowCodePoint); index += 6; // skip 6 chars continue; } } } success = false; // invalid surrogate pair return ""; } s.Append(ConvertFromUtf32((int)codePoint)); // skip 4 chars index += 4; } else break; } } else s.Append(c); } if (!complete) { success = false; return null; } return s.ToString(); } private static string ConvertFromUtf32(int utf32) { // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm if (utf32 < 0 || utf32 > 0x10FFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); if (0xD800 <= utf32 && utf32 <= 0xDFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); if (utf32 < 0x10000) return new string((char)utf32, 1); utf32 -= 0x10000; return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); } static object ParseNumber(char[] json, ref int index, ref bool success) { EatWhitespace(json, ref index); int lastIndex = GetLastIndexOfNumber(json, index); int charLength = (lastIndex - index) + 1; object returnNumber; string str = new string(json, index, charLength); if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) { double number; success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } else { long number; success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } index = lastIndex + 1; return returnNumber; } static int GetLastIndexOfNumber(char[] json, int index) { int lastIndex; for (lastIndex = index; lastIndex < json.Length; lastIndex++) if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; return lastIndex - 1; } static void EatWhitespace(char[] json, ref int index) { for (; index < json.Length; index++) if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; } static int LookAhead(char[] json, int index) { int saveIndex = index; return NextToken(json, ref saveIndex); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] static int NextToken(char[] json, ref int index) { EatWhitespace(json, ref index); if (index == json.Length) return TOKEN_NONE; char c = json[index]; index++; switch (c) { case '{': return TOKEN_CURLY_OPEN; case '}': return TOKEN_CURLY_CLOSE; case '[': return TOKEN_SQUARED_OPEN; case ']': return TOKEN_SQUARED_CLOSE; case ',': return TOKEN_COMMA; case '"': return TOKEN_STRING; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN_NUMBER; case ':': return TOKEN_COLON; } index--; int remainingLength = json.Length - index; // false if (remainingLength >= 5) { if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') { index += 5; return TOKEN_FALSE; } } // true if (remainingLength >= 4) { if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { index += 4; return TOKEN_TRUE; } } // null if (remainingLength >= 4) { if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { index += 4; return TOKEN_NULL; } } return TOKEN_NONE; } static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder, bool excludeNullValues) { bool success = true; string stringValue = value as string; if (stringValue != null) success = SerializeString(stringValue, builder); else { IDictionary dict = value as IDictionary; if (dict != null) { success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder, excludeNullValues); } else { IDictionary stringDictionary = value as IDictionary; if (stringDictionary != null) { success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder, excludeNullValues); } else { IEnumerable enumerableValue = value as IEnumerable; if (enumerableValue != null) success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder, excludeNullValues); else if (IsNumeric(value)) success = SerializeNumber(value, builder); else if (value is bool) builder.Append((bool)value ? "true" : "false"); else if (value == null) builder.Append("null"); else { object serializedObject; success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); if (success) SerializeValue(jsonSerializerStrategy, serializedObject, builder, excludeNullValues); } } } } return success; } static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder, bool excludeNullValues) { builder.Append("{"); IEnumerator ke = keys.GetEnumerator(); IEnumerator ve = values.GetEnumerator(); bool first = true; while (ke.MoveNext() && ve.MoveNext()) { object key = ke.Current; object value = ve.Current; if (value == null && excludeNullValues) { continue; } if (!first) builder.Append(","); string stringKey = key as string; if (stringKey != null) SerializeString(jsonSerializerStrategy.MapDictionaryKeyToFieldName(stringKey), builder); else if (!SerializeValue(jsonSerializerStrategy, value, builder, excludeNullValues)) return false; builder.Append(":"); if (!SerializeValue(jsonSerializerStrategy, value, builder, excludeNullValues)) return false; first = false; } builder.Append("}"); return true; } static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder, bool excludeNullValues) { builder.Append("["); bool first = true; foreach (object value in anArray) { if (!first) builder.Append(","); if (!SerializeValue(jsonSerializerStrategy, value, builder, excludeNullValues)) return false; first = false; } builder.Append("]"); return true; } static bool SerializeString(string aString, StringBuilder builder) { // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) if (aString.IndexOfAny(EscapeCharacters) == -1) { builder.Append('"'); builder.Append(aString); builder.Append('"'); return true; } builder.Append('"'); int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; // Non ascii characters are fine, buffer them up and send them to the builder // in larger chunks if possible. The escape table is a 1:1 translation table // with \0 [default(char)] denoting a safe character. if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) { safeCharacterCount++; } else { if (safeCharacterCount > 0) { builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); safeCharacterCount = 0; } builder.Append('\\'); builder.Append(EscapeTable[c]); } } if (safeCharacterCount > 0) { builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); } builder.Append('"'); return true; } static bool SerializeNumber(object number, StringBuilder builder) { if (number is long) builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); else if (number is ulong) builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); else if (number is int) builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); else if (number is uint) builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); else if (number is decimal) builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); else if (number is float) builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); else builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); return true; } /// /// Determines if a given object is numeric in any way /// (can be integer, double, null, etc). /// static bool IsNumeric(object value) { if (value is sbyte) return true; if (value is byte) return true; if (value is short) return true; if (value is ushort) return true; if (value is int) return true; if (value is uint) return true; if (value is long) return true; if (value is ulong) return true; if (value is float) return true; if (value is double) return true; if (value is decimal) return true; return false; } private static IJsonSerializerStrategy _currentJsonSerializerStrategy; public static IJsonSerializerStrategy CurrentJsonSerializerStrategy { get { return _currentJsonSerializerStrategy ?? (_currentJsonSerializerStrategy = #if SIMPLE_JSON_DATACONTRACT DataContractJsonSerializerStrategy #else PocoJsonSerializerStrategy #endif ); } set { _currentJsonSerializerStrategy = value; } } private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; [EditorBrowsable(EditorBrowsableState.Advanced)] public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy { get { return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); } } #if SIMPLE_JSON_DATACONTRACT private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy { get { return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); } } #endif } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif interface IJsonSerializerStrategy { [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] bool TrySerializeNonPrimitiveObject(object input, out object output); object DeserializeObject(object value, Type type, DateTimeStyles dateTimeStyles); string MapDictionaryKeyToFieldName(string stringKey); } #if SIMPLE_JSON_INTERNAL internal #else public #endif class NamedConstructorArgs { public Type Type { get; private set; } public string[] ParameterNames { get; private set; } public NamedConstructorArgs(Type type, string[] parameterNames) { this.Type = type; this.ParameterNames = parameterNames; } } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class PocoJsonSerializerStrategy : IJsonSerializerStrategy { internal static IDictionary DefaultConstructorCache; internal static IDictionary TypedConstructorCache; internal static IDictionary> GetCache; internal static IDictionary>> SetCache; internal static IDictionary> ParameterTypeCache; internal static readonly Type[] EmptyTypes = ArrayCache.Empty(); internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; protected static string FullDateTimeWithOffset = @"yyyy-MM-dd\THH:mm:ss.fffffffzzz"; protected static string FullDateTimeUtc = @"yyyy-MM-dd\THH:mm:ss.fffffff\Z"; private static readonly string[] Iso8601Format = new string[] { FullDateTimeWithOffset, @"yyyy-MM-dd\THH:mm:ss.FFFFFFFK", FullDateTimeUtc, @"yyyy-MM-dd\THH:mm:ss\Z", @"yyyy-MM-dd\THH:mm:ssK" }; static PocoJsonSerializerStrategy() { DefaultConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); TypedConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); ParameterTypeCache = new ReflectionUtils.ThreadSafeDictionary>(ParameterTypeFactory); } public string MapDictionaryKeyToFieldName(string stringKey) { return this.MapClrMemberNameToJsonFieldName(stringKey); } protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) { return clrPropertyName; } internal static ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) { return ReflectionUtils.GetConstructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); } internal static ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(NamedConstructorArgs constructorArgs) { return ReflectionUtils.GetNamedConstructor(constructorArgs.Type, constructorArgs.ParameterNames); } internal static IDictionary GetterValueFactory(Type type) { IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead && !Nancy.Helpers.ReflectionUtils.IsIndexedProperty(propertyInfo)) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (getMethod.IsStatic || !getMethod.IsPublic) continue; result[propertyInfo.Name] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[fieldInfo.Name] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal static IDictionary> SetterValueFactory(Type type) { IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite && !Nancy.Helpers.ReflectionUtils.IsIndexedProperty(propertyInfo)) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (setMethod.IsStatic || !setMethod.IsPublic) continue; result[propertyInfo.Name] = new KeyValuePair( propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[fieldInfo.Name] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } return result; } internal static IDictionary ParameterTypeFactory(NamedConstructorArgs constructor) { IDictionary result = new Dictionary(); foreach (ParameterInfo paramterInfo in ReflectionUtils .GetNamedConstructorInfo(constructor.Type, constructor.ParameterNames) .GetParameters()) { result[paramterInfo.Name] = paramterInfo.ParameterType; } return result; } public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) { return this.TrySerializeKnownTypes(input, out output) || this.TrySerializeUnknownTypes(input, out output); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public virtual object DeserializeObject(object value, Type type, DateTimeStyles dateTimeStyles) { if (type == null) throw new ArgumentNullException("type"); string str = value as string; if (type == typeof (Guid) && string.IsNullOrEmpty(str)) return default(Guid); if (value == null) return null; object obj = null; if (str != null) { if (str.Length != 0) // We know it can't be null now. { if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, dateTimeStyles); if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, dateTimeStyles); if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) return new Guid(str); if (type == typeof(Uri)) { bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); Uri result; if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) return result; return null; } if (type == typeof(string)) return str; // allows assigning "123" to int? if (ReflectionUtils.IsNullableType(type)) { return ReflectionUtils.ToNullableType(str, type); } return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); } else { if (type == typeof(Guid)) obj = default(Guid); else if (ReflectionUtils.IsNullableType(type)) obj = null; else obj = str; } // Empty string case if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) return str; } else if (value is bool) return value; bool valueIsLong = value is long; bool valueIsDouble = value is double; if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) return value; if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) { obj = type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) || type == (typeof(ushort)) ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) : value; } else { IDictionary objects = value as IDictionary; if (objects != null) { IDictionary jsonObject = objects; if (ReflectionUtils.IsTypeDictionary(type)) { // if dictionary then Type[] types = ReflectionUtils.GetGenericTypeArguments(type); Type keyType = types[0]; Type valueType = types[1]; Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); IDictionary dict = (IDictionary)DefaultConstructorCache[genericType](); foreach (KeyValuePair kvp in jsonObject) dict.Add(kvp.Key, this.DeserializeObject(kvp.Value, valueType, dateTimeStyles)); obj = dict; } else { if (type == typeof(object)) { obj = value; } else { var constructor = DefaultConstructorCache[type]; if (constructor != null) { obj = constructor(); foreach (KeyValuePair> setter in SetCache[type]) { object jsonValue; if (jsonObject.TryGetValue(setter.Key, out jsonValue)) { jsonValue = this.DeserializeObject(jsonValue, setter.Value.Key, dateTimeStyles); setter.Value.Value(obj, jsonValue); } } } else { var args = new NamedConstructorArgs(type, jsonObject.Keys.ToArray()); constructor = TypedConstructorCache[args]; if (constructor == null) { throw new ValidConstructorNotFoundException(type); } var arguments = new object[jsonObject.Count]; var index = 0; foreach (KeyValuePair parameterType in ParameterTypeCache[args]) { object jsonValue; if (jsonObject.TryGetValue(parameterType.Key, out jsonValue)) { jsonValue = this.DeserializeObject(jsonValue, parameterType.Value, dateTimeStyles); arguments[index] = jsonValue; index++; } } obj = constructor(arguments); } } } } else { IList valueAsList = value as IList; if (valueAsList != null) { IList jsonObject = valueAsList; IList list = null; if (type.IsArray) { list = (IList)DefaultConstructorCache[type](jsonObject.Count); int i = 0; foreach (object o in jsonObject) list[i++] = this.DeserializeObject(o, type.GetElementType(), dateTimeStyles); } else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) { Type innerType = ReflectionUtils.GetGenericListElementType(type); list = (IList)(DefaultConstructorCache[type] ?? DefaultConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); foreach (object o in jsonObject) list.Add(this.DeserializeObject(o, innerType, dateTimeStyles)); } obj = list; } } return obj; } if (ReflectionUtils.IsNullableType(type)) return ReflectionUtils.ToNullableType(obj, type); return obj; } protected virtual object SerializeEnum(Enum p) { return Convert.ToDouble(p, CultureInfo.InvariantCulture); } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeKnownTypes(object input, out object output) { bool returnValue = true; if (input is DateTime) output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is DateTimeOffset) output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is Guid) output = ((Guid)input).ToString("D"); else if (input is Uri) output = input.ToString(); else { Enum inputEnum = input as Enum; if (inputEnum != null) output = this.SerializeEnum(inputEnum); else { returnValue = false; output = null; } } return returnValue; } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeUnknownTypes(object input, out object output) { if (input == null) throw new ArgumentNullException("input"); output = null; Type type = input.GetType(); if (type.FullName == null) return false; IDictionary obj = new JsonObject(); IDictionary getters = GetCache[type]; foreach (KeyValuePair getter in getters) { if (getter.Value != null) obj.Add(this.MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); } output = obj; return true; } } #if SIMPLE_JSON_DATACONTRACT [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy { public DataContractJsonSerializerStrategy() { GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } internal override IDictionary GetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.GetterValueFactory(type); string jsonKey; IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal override IDictionary> SetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.SetterValueFactory(type); string jsonKey; IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } // todo implement sorting for DATACONTRACT. return result; } private static bool CanAdd(MemberInfo info, out string jsonKey) { jsonKey = null; if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) return false; DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); if (dataMemberAttribute == null) return false; jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; return true; } } #endif internal class ValidConstructorNotFoundException : Exception { public ValidConstructorNotFoundException(Type type) : base(string.Format("No valid constructor could be found for {0}", type.FullName)) { } } // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules // that might be in place in the target project. [GeneratedCode("reflection-utils", "1.0.0")] #if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC public #else internal #endif class ReflectionUtils { private static readonly object[] EmptyObjects = new object[] { }; public delegate object GetDelegate(object source); public delegate void SetDelegate(object source, object value); public delegate object ConstructorDelegate(params object[] args); public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); #if SIMPLE_JSON_TYPEINFO public static TypeInfo GetTypeInfo(Type type) { return type.GetTypeInfo(); } #else public static Type GetTypeInfo(Type type) { return type; } #endif public static Attribute GetAttribute(MemberInfo info, Type type) { #if SIMPLE_JSON_TYPEINFO if (info == null || type == null || !info.IsDefined(type)) return null; return info.GetCustomAttribute(type); #else if (info == null || type == null || !Attribute.IsDefined(info, type)) return null; return Attribute.GetCustomAttribute(info, type); #endif } public static Type GetGenericListElementType(Type type) { IEnumerable interfaces; #if SIMPLE_JSON_TYPEINFO interfaces = type.GetTypeInfo().ImplementedInterfaces; #else interfaces = type.GetInterfaces(); #endif foreach (Type implementedInterface in interfaces) { if (IsTypeGeneric(implementedInterface) && implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) { return GetGenericTypeArguments(implementedInterface)[0]; } } return GetGenericTypeArguments(type)[0]; } public static Attribute GetAttribute(Type objectType, Type attributeType) { #if SIMPLE_JSON_TYPEINFO if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) return null; return objectType.GetTypeInfo().GetCustomAttribute(attributeType); #else if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) return null; return Attribute.GetCustomAttribute(objectType, attributeType); #endif } public static Type[] GetGenericTypeArguments(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().GenericTypeArguments; #else return type.GetGenericArguments(); #endif } public static bool IsTypeGeneric(Type type) { return GetTypeInfo(type).IsGenericType; } public static bool IsTypeGenericeCollectionInterface(Type type) { if (!IsTypeGeneric(type)) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>) #if SIMPLE_JSON_READONLY_COLLECTIONS || genericDefinition == typeof(IReadOnlyCollection<>) || genericDefinition == typeof(IReadOnlyList<>) #endif ); } public static bool IsAssignableFrom(Type type1, Type type2) { return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); } public static bool IsTypeDictionary(Type type) { #if SIMPLE_JSON_TYPEINFO if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) return true; #else if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) return true; #endif if (!GetTypeInfo(type).IsGenericType) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return genericDefinition == typeof(IDictionary<,>); } public static bool IsNullableType(Type type) { return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } public static object ToNullableType(object obj, Type nullableType) { return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); } public static bool IsValueType(Type type) { return GetTypeInfo(type).IsValueType; } public static IEnumerable GetConstructors(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().DeclaredConstructors; #else return type.GetConstructors(); #endif } public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) { IEnumerable constructorInfos = GetConstructors(type); int i; bool matches; foreach (ConstructorInfo constructorInfo in constructorInfos) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (argsType.Length != parameters.Length) continue; i = 0; matches = true; foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) { if (parameterInfo.ParameterType != argsType[i]) { matches = false; break; } } if (matches) return constructorInfo; } return null; } public static ConstructorInfo GetNamedConstructorInfo(Type type, params string[] parameterNames) { IEnumerable constructorInfos = GetConstructors(type); foreach (ConstructorInfo constructorInfo in constructorInfos) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameterNames.Length != parameters.Length) continue; var i = 0; var matches = true; foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) { if (!string.Equals(parameterInfo.Name, parameterNames[i], StringComparison.OrdinalIgnoreCase)) { matches = false; break; } i++; } if (matches) return constructorInfo; } return null; } public static IEnumerable GetProperties(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeProperties(); #else return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static IEnumerable GetFields(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeFields(); #else return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.GetMethod; #else return propertyInfo.GetGetMethod(true); #endif } public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.SetMethod; #else return propertyInfo.GetSetMethod(true); #endif } public static ConstructorDelegate GetConstructor(ConstructorInfo constructorInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(constructorInfo); #else return GetConstructorByExpression(constructorInfo); #endif } public static ConstructorDelegate GetConstructor(Type type, params Type[] argsType) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(type, argsType); #else return GetConstructorByExpression(type, argsType); #endif } public static ConstructorDelegate GetNamedConstructor(Type type, params string[] parameterNames) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetNamedConstructorByReflection(type, parameterNames); #else return GetNamedConstructorByExpression(type, parameterNames); #endif } public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) { return delegate(object[] args) { return constructorInfo.Invoke(args); }; } public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); } public static ConstructorDelegate GetNamedConstructorByReflection(Type type, params string[] parameterNames) { ConstructorInfo constructorInfo = GetNamedConstructorInfo(type, parameterNames); return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) { ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); Expression[] argsExp = new Expression[paramsInfo.Length]; for (int i = 0; i < paramsInfo.Length; i++) { Expression index = Expression.Constant(i); Type paramType = paramsInfo[i].ParameterType; Expression paramAccessorExp = Expression.ArrayIndex(param, index); Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); argsExp[i] = paramCastExp; } NewExpression newExp = Expression.New(constructorInfo, argsExp); Expression> lambda = Expression.Lambda>(newExp, param); Func compiledLambda = lambda.Compile(); return delegate(object[] args) { return compiledLambda(args); }; } public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); } public static ConstructorDelegate GetNamedConstructorByExpression(Type type, params string[] parameterNames) { ConstructorInfo constructorInfo = GetNamedConstructorInfo(type, parameterNames); return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); } #endif public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(propertyInfo); #else return GetGetMethodByExpression(propertyInfo); #endif } public static GetDelegate GetGetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(fieldInfo); #else return GetGetMethodByExpression(fieldInfo); #endif } public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; } public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source) { return fieldInfo.GetValue(source); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } #endif public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(propertyInfo); #else return GetSetMethodByExpression(propertyInfo); #endif } public static SetDelegate GetSetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(fieldInfo); #else return GetSetMethodByExpression(fieldInfo); #endif } public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; } public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); Action compiled = Expression.Lambda>( Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static BinaryExpression Assign(Expression left, Expression right) { #if SIMPLE_JSON_TYPEINFO return Expression.Assign(left, right); #else MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); BinaryExpression assignExpr = Expression.Add(left, right, assign); return assignExpr; #endif } private static class Assigner { public static T Assign(ref T left, T right) { return (left = right); } } #endif public sealed class ThreadSafeDictionary : IDictionary { private readonly object _lock = new object(); private readonly ThreadSafeDictionaryValueFactory _valueFactory; private Dictionary _dictionary; public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) { this._valueFactory = valueFactory; } private TValue Get(TKey key) { if (this._dictionary == null) return this.AddValue(key); TValue value; if (!this._dictionary.TryGetValue(key, out value)) return this.AddValue(key); return value; } private TValue AddValue(TKey key) { TValue value = this._valueFactory(key); lock (this._lock) { if (this._dictionary == null) { this._dictionary = new Dictionary(); this._dictionary[key] = value; } else { TValue val; if (this._dictionary.TryGetValue(key, out val)) return val; Dictionary dict = new Dictionary(this._dictionary); dict[key] = value; this._dictionary = dict; } } return value; } public void Add(TKey key, TValue value) { throw new NotImplementedException(); } public bool ContainsKey(TKey key) { return this._dictionary.ContainsKey(key); } public ICollection Keys { get { return this._dictionary.Keys; } } public bool Remove(TKey key) { throw new NotImplementedException(); } public bool TryGetValue(TKey key, out TValue value) { value = this[key]; return true; } public ICollection Values { get { return this._dictionary.Values; } } public TValue this[TKey key] { get { return this.Get(key); } set { throw new NotImplementedException(); } } public void Add(KeyValuePair item) { throw new NotImplementedException(); } public void Clear() { throw new NotImplementedException(); } public bool Contains(KeyValuePair item) { throw new NotImplementedException(); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { throw new NotImplementedException(); } public int Count { get { return this._dictionary.Count; } } public bool IsReadOnly { get { throw new NotImplementedException(); } } public bool Remove(KeyValuePair item) { throw new NotImplementedException(); } public IEnumerator> GetEnumerator() { return this._dictionary.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this._dictionary.GetEnumerator(); } } } // ReSharper restore LoopCanBeConvertedToQuery // ReSharper restore RedundantExplicitArrayCreation // ReSharper restore SuggestUseVarKeywordEvident } ================================================ FILE: src/Nancy/Json/SimpleJson.cs ================================================ //----------------------------------------------------------------------- // // Copyright (c) 2011, The Outercurve Foundation. // // Licensed under the MIT License (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.opensource.org/licenses/mit-license.php // // 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. // // Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) // https://github.com/facebook-csharp-sdk/simple-json //----------------------------------------------------------------------- // VERSION: 0.38.0 // NOTE: uncomment the following line to make SimpleJson class internal. #define SIMPLE_JSON_INTERNAL // NOTE: uncomment the following line to make JsonArray and JsonObject class internal. #define SIMPLE_JSON_OBJARRAYINTERNAL // NOTE: uncomment the following line to enable dynamic support. #define SIMPLE_JSON_DYNAMIC // NOTE: uncomment the following line to enable DataContract support. //#define SIMPLE_JSON_DATACONTRACT // NOTE: uncomment the following line to enable IReadOnlyCollection and IReadOnlyList support. //#define SIMPLE_JSON_READONLY_COLLECTIONS // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). // define if you are using .net framework <= 3.0 or < WP7.5 //#define SIMPLE_JSON_NO_LINQ_EXPRESSION // NOTE: uncomment the following line if you are compiling under Window Metro style application/library. // usually already defined in properties //#define NETFX_CORE; // If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; // original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html #if NETFX_CORE || NETSTANDARD1_6 #define SIMPLE_JSON_TYPEINFO #endif using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; #if !SIMPLE_JSON_NO_LINQ_EXPRESSION using System.Linq.Expressions; #endif using System.ComponentModel; using System.Diagnostics.CodeAnalysis; #if SIMPLE_JSON_DYNAMIC using System.Dynamic; #endif using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using System.Text; using Nancy.Reflection; // ReSharper disable LoopCanBeConvertedToQuery // ReSharper disable RedundantExplicitArrayCreation // ReSharper disable SuggestUseVarKeywordEvident namespace Nancy { /// /// Represents the json array. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonArray : List { /// /// Initializes a new instance of the class. /// public JsonArray() { } /// /// Initializes a new instance of the class. /// /// The capacity of the json array. public JsonArray(int capacity) : base(capacity) { } /// /// The json representation of the array. /// /// The json representation of the array. public override string ToString() { return SimpleJson.SerializeObject(this) ?? string.Empty; } } /// /// Represents the json object. /// [GeneratedCode("simple-json", "1.0.0")] [EditorBrowsable(EditorBrowsableState.Never)] [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] #if SIMPLE_JSON_OBJARRAYINTERNAL internal #else public #endif class JsonObject : #if SIMPLE_JSON_DYNAMIC DynamicObject, #endif IDictionary { /// /// The internal member dictionary. /// private readonly Dictionary _members; /// /// Initializes a new instance of . /// public JsonObject() { _members = new Dictionary(); } /// /// Initializes a new instance of . /// /// The implementation to use when comparing keys, or null to use the default for the type of the key. public JsonObject(IEqualityComparer comparer) { _members = new Dictionary(comparer); } /// /// Gets the at the specified index. /// /// public object this[int index] { get { return GetAtIndex(_members, index); } } internal static object GetAtIndex(IDictionary obj, int index) { if (obj == null) throw new ArgumentNullException("obj"); if (index >= obj.Count) throw new ArgumentOutOfRangeException("index"); int i = 0; foreach (KeyValuePair o in obj) if (i++ == index) return o.Value; return null; } /// /// Adds the specified key. /// /// The key. /// The value. public void Add(string key, object value) { _members.Add(key, value); } /// /// Determines whether the specified key contains key. /// /// The key. /// /// true if the specified key contains key; otherwise, false. /// public bool ContainsKey(string key) { return _members.ContainsKey(key); } /// /// Gets the keys. /// /// The keys. public ICollection Keys { get { return _members.Keys; } } /// /// Removes the specified key. /// /// The key. /// public bool Remove(string key) { return _members.Remove(key); } /// /// Tries the get value. /// /// The key. /// The value. /// public bool TryGetValue(string key, out object value) { return _members.TryGetValue(key, out value); } /// /// Gets the values. /// /// The values. public ICollection Values { get { return _members.Values; } } /// /// Gets or sets the with the specified key. /// /// public object this[string key] { get { return _members[key]; } set { _members[key] = value; } } /// /// Adds the specified item. /// /// The item. public void Add(KeyValuePair item) { _members.Add(item.Key, item.Value); } /// /// Clears this instance. /// public void Clear() { _members.Clear(); } /// /// Determines whether [contains] [the specified item]. /// /// The item. /// /// true if [contains] [the specified item]; otherwise, false. /// public bool Contains(KeyValuePair item) { object value; return _members.TryGetValue(item.Key, out value) && value == item.Value; } /// /// Copies to. /// /// The array. /// Index of the array. public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); int num = Count; foreach (KeyValuePair kvp in this) { array[arrayIndex++] = kvp; if (--num <= 0) return; } } /// /// Gets the count. /// /// The count. public int Count { get { return _members.Count; } } /// /// Gets a value indicating whether this instance is read only. /// /// /// true if this instance is read only; otherwise, false. /// public bool IsReadOnly { get { return false; } } /// /// Removes the specified item. /// /// The item. /// public bool Remove(KeyValuePair item) { return _members.Remove(item.Key); } /// /// Gets the enumerator. /// /// public IEnumerator> GetEnumerator() { return _members.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return _members.GetEnumerator(); } /// /// Returns a json that represents the current . /// /// /// A json that represents the current . /// public override string ToString() { return SimpleJson.SerializeObject(this); } #if SIMPLE_JSON_DYNAMIC /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. /// The result of the type conversion operation. /// /// Alwasy returns true. /// public override bool TryConvert(ConvertBinder binder, out object result) { // if (binder == null) throw new ArgumentNullException("binder"); // Type targetType = binder.Type; if ((targetType == typeof(IEnumerable)) || (targetType == typeof(IEnumerable>)) || (targetType == typeof(IDictionary)) || (targetType == typeof(IDictionary))) { result = this; return true; } return base.TryConvert(binder, out result); } /// /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. /// /// Provides information about the deletion. /// /// Alwasy returns true. /// public override bool TryDeleteMember(DeleteMemberBinder binder) { // if (binder == null) throw new ArgumentNullException("binder"); // return _members.Remove(binder.Name); } /// /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. /// The result of the index operation. /// /// Alwasy returns true. /// public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { result = ((IDictionary)this)[(string)indexes[0]]; return true; } result = null; return true; } /// /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . /// /// Alwasy returns true. /// public override bool TryGetMember(GetMemberBinder binder, out object result) { object value; if (_members.TryGetValue(binder.Name, out value)) { result = value; return true; } result = null; return true; } /// /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. /// /// Provides information about the operation. /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. /// public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { if (indexes == null) throw new ArgumentNullException("indexes"); if (indexes.Length == 1) { ((IDictionary)this)[(string)indexes[0]] = value; return true; } return base.TrySetIndex(binder, indexes, value); } /// /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// public override bool TrySetMember(SetMemberBinder binder, object value) { // if (binder == null) throw new ArgumentNullException("binder"); // _members[binder.Name] = value; return true; } /// /// Returns the enumeration of all dynamic member names. /// /// /// A sequence that contains dynamic member names. /// public override IEnumerable GetDynamicMemberNames() { foreach (var key in Keys) yield return key; } #endif } } namespace Nancy { /// /// This class encodes and decodes JSON strings. /// Spec. details, see http://www.json.org/ /// /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). /// All numbers are parsed to doubles. /// [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif static class SimpleJson { private const int TOKEN_NONE = 0; private const int TOKEN_CURLY_OPEN = 1; private const int TOKEN_CURLY_CLOSE = 2; private const int TOKEN_SQUARED_OPEN = 3; private const int TOKEN_SQUARED_CLOSE = 4; private const int TOKEN_COLON = 5; private const int TOKEN_COMMA = 6; private const int TOKEN_STRING = 7; private const int TOKEN_NUMBER = 8; private const int TOKEN_TRUE = 9; private const int TOKEN_FALSE = 10; private const int TOKEN_NULL = 11; private const int BUILDER_CAPACITY = 2000; private static readonly char[] EscapeTable; private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; private static readonly string EscapeCharactersString = new string(EscapeCharacters); static SimpleJson() { EscapeTable = new char[93]; EscapeTable['"'] = '"'; EscapeTable['\\'] = '\\'; EscapeTable['\b'] = 'b'; EscapeTable['\f'] = 'f'; EscapeTable['\n'] = 'n'; EscapeTable['\r'] = 'r'; EscapeTable['\t'] = 't'; } /// /// Parses the string json into a value /// /// A JSON string. /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false public static object DeserializeObject(string json) { object obj; if (TryDeserializeObject(json, out obj)) return obj; throw new SerializationException("Invalid JSON string"); } /// /// Try parsing the json string into a value. /// /// /// A JSON string. /// /// /// The object. /// /// /// Returns true if successfull otherwise false. /// [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] public static bool TryDeserializeObject(string json, out object obj) { bool success = true; if (json != null) { char[] charArray = json.ToCharArray(); int index = 0; obj = ParseValue(charArray, ref index, ref success); } else obj = null; return success; } public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) { object jsonObject = DeserializeObject(json); return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) ? jsonObject : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); } public static object DeserializeObject(string json, Type type) { return DeserializeObject(json, type, null); } public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) { return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); } public static T DeserializeObject(string json) { return (T)DeserializeObject(json, typeof(T), null); } /// /// Converts a IDictionary<string,object> / IList<object> object into a JSON string /// /// A IDictionary<string,object> / IList<object> /// Serializer strategy to use /// A JSON encoded string, or null if object 'json' is not serializable public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) { StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); bool success = SerializeValue(jsonSerializerStrategy, json, builder); return (success ? builder.ToString() : null); } public static string SerializeObject(object json) { return SerializeObject(json, CurrentJsonSerializerStrategy); } public static string EscapeToJavascriptString(string jsonString) { if (string.IsNullOrEmpty(jsonString)) return jsonString; StringBuilder sb = new StringBuilder(); char c; for (int i = 0; i < jsonString.Length; ) { c = jsonString[i++]; if (c == '\\') { int remainingLength = jsonString.Length - i; if (remainingLength >= 2) { char lookahead = jsonString[i]; if (lookahead == '\\') { sb.Append('\\'); ++i; } else if (lookahead == '"') { sb.Append("\""); ++i; } else if (lookahead == 't') { sb.Append('\t'); ++i; } else if (lookahead == 'b') { sb.Append('\b'); ++i; } else if (lookahead == 'n') { sb.Append('\n'); ++i; } else if (lookahead == 'r') { sb.Append('\r'); ++i; } } } else { sb.Append(c); } } return sb.ToString(); } static IDictionary ParseObject(char[] json, ref int index, ref bool success) { IDictionary table = new JsonObject(); int token; // { NextToken(json, ref index); bool done = false; while (!done) { token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_CURLY_CLOSE) { NextToken(json, ref index); return table; } else { // name string name = ParseString(json, ref index, ref success); if (!success) { success = false; return null; } // : token = NextToken(json, ref index); if (token != TOKEN_COLON) { success = false; return null; } // value object value = ParseValue(json, ref index, ref success); if (!success) { success = false; return null; } table[name] = value; } } return table; } static JsonArray ParseArray(char[] json, ref int index, ref bool success) { JsonArray array = new JsonArray(); // [ NextToken(json, ref index); bool done = false; while (!done) { int token = LookAhead(json, index); if (token == TOKEN_NONE) { success = false; return null; } else if (token == TOKEN_COMMA) NextToken(json, ref index); else if (token == TOKEN_SQUARED_CLOSE) { NextToken(json, ref index); break; } else { object value = ParseValue(json, ref index, ref success); if (!success) return null; array.Add(value); } } return array; } static object ParseValue(char[] json, ref int index, ref bool success) { switch (LookAhead(json, index)) { case TOKEN_STRING: return ParseString(json, ref index, ref success); case TOKEN_NUMBER: return ParseNumber(json, ref index, ref success); case TOKEN_CURLY_OPEN: return ParseObject(json, ref index, ref success); case TOKEN_SQUARED_OPEN: return ParseArray(json, ref index, ref success); case TOKEN_TRUE: NextToken(json, ref index); return true; case TOKEN_FALSE: NextToken(json, ref index); return false; case TOKEN_NULL: NextToken(json, ref index); return null; case TOKEN_NONE: break; } success = false; return null; } static string ParseString(char[] json, ref int index, ref bool success) { StringBuilder s = new StringBuilder(BUILDER_CAPACITY); char c; EatWhitespace(json, ref index); // " c = json[index++]; bool complete = false; while (!complete) { if (index == json.Length) break; c = json[index++]; if (c == '"') { complete = true; break; } else if (c == '\\') { if (index == json.Length) break; c = json[index++]; if (c == '"') s.Append('"'); else if (c == '\\') s.Append('\\'); else if (c == '/') s.Append('/'); else if (c == 'b') s.Append('\b'); else if (c == 'f') s.Append('\f'); else if (c == 'n') s.Append('\n'); else if (c == 'r') s.Append('\r'); else if (c == 't') s.Append('\t'); else if (c == 'u') { int remainingLength = json.Length - index; if (remainingLength >= 4) { // parse the 32 bit hex into an integer codepoint uint codePoint; if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) return ""; // convert the integer codepoint to a unicode char and add to string if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate { index += 4; // skip 4 chars remainingLength = json.Length - index; if (remainingLength >= 6) { uint lowCodePoint; if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) { if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate { s.Append((char)codePoint); s.Append((char)lowCodePoint); index += 6; // skip 6 chars continue; } } } success = false; // invalid surrogate pair return ""; } s.Append(ConvertFromUtf32((int)codePoint)); // skip 4 chars index += 4; } else break; } } else s.Append(c); } if (!complete) { success = false; return null; } return s.ToString(); } private static string ConvertFromUtf32(int utf32) { // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm if (utf32 < 0 || utf32 > 0x10FFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); if (0xD800 <= utf32 && utf32 <= 0xDFFF) throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); if (utf32 < 0x10000) return new string((char)utf32, 1); utf32 -= 0x10000; return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); } static object ParseNumber(char[] json, ref int index, ref bool success) { EatWhitespace(json, ref index); int lastIndex = GetLastIndexOfNumber(json, index); int charLength = (lastIndex - index) + 1; object returnNumber; string str = new string(json, index, charLength); if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) { double number; success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } else { long number; success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); returnNumber = number; } index = lastIndex + 1; return returnNumber; } static int GetLastIndexOfNumber(char[] json, int index) { int lastIndex; for (lastIndex = index; lastIndex < json.Length; lastIndex++) if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; return lastIndex - 1; } static void EatWhitespace(char[] json, ref int index) { for (; index < json.Length; index++) if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; } static int LookAhead(char[] json, int index) { int saveIndex = index; return NextToken(json, ref saveIndex); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] static int NextToken(char[] json, ref int index) { EatWhitespace(json, ref index); if (index == json.Length) return TOKEN_NONE; char c = json[index]; index++; switch (c) { case '{': return TOKEN_CURLY_OPEN; case '}': return TOKEN_CURLY_CLOSE; case '[': return TOKEN_SQUARED_OPEN; case ']': return TOKEN_SQUARED_CLOSE; case ',': return TOKEN_COMMA; case '"': return TOKEN_STRING; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN_NUMBER; case ':': return TOKEN_COLON; } index--; int remainingLength = json.Length - index; // false if (remainingLength >= 5) { if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') { index += 5; return TOKEN_FALSE; } } // true if (remainingLength >= 4) { if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { index += 4; return TOKEN_TRUE; } } // null if (remainingLength >= 4) { if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { index += 4; return TOKEN_NULL; } } return TOKEN_NONE; } static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) { bool success = true; string stringValue = value as string; if (stringValue != null) success = SerializeString(stringValue, builder); else { IDictionary dict = value as IDictionary; if (dict != null) { success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); } else { IDictionary stringDictionary = value as IDictionary; if (stringDictionary != null) { success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); } else { IEnumerable enumerableValue = value as IEnumerable; if (enumerableValue != null) success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); else if (IsNumeric(value)) success = SerializeNumber(value, builder); else if (value is bool) builder.Append((bool)value ? "true" : "false"); else if (value == null) builder.Append("null"); else { object serializedObject; success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); if (success) SerializeValue(jsonSerializerStrategy, serializedObject, builder); } } } } return success; } static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) { builder.Append("{"); IEnumerator ke = keys.GetEnumerator(); IEnumerator ve = values.GetEnumerator(); bool first = true; while (ke.MoveNext() && ve.MoveNext()) { object key = ke.Current; object value = ve.Current; if (!first) builder.Append(","); string stringKey = key as string; if (stringKey != null) SerializeString(stringKey, builder); else if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; builder.Append(":"); if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; first = false; } builder.Append("}"); return true; } static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) { builder.Append("["); bool first = true; foreach (object value in anArray) { if (!first) builder.Append(","); if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; first = false; } builder.Append("]"); return true; } static bool SerializeString(string aString, StringBuilder builder) { // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) if (aString.IndexOfAny(EscapeCharacters) == -1) { builder.Append('"'); builder.Append(aString); builder.Append('"'); return true; } builder.Append('"'); int safeCharacterCount = 0; char[] charArray = aString.ToCharArray(); for (int i = 0; i < charArray.Length; i++) { char c = charArray[i]; // Non ascii characters are fine, buffer them up and send them to the builder // in larger chunks if possible. The escape table is a 1:1 translation table // with \0 [default(char)] denoting a safe character. if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) { safeCharacterCount++; } else { if (safeCharacterCount > 0) { builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); safeCharacterCount = 0; } builder.Append('\\'); builder.Append(EscapeTable[c]); } } if (safeCharacterCount > 0) { builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); } builder.Append('"'); return true; } static bool SerializeNumber(object number, StringBuilder builder) { if (number is long) builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); else if (number is ulong) builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); else if (number is int) builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); else if (number is uint) builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); else if (number is decimal) builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); else if (number is float) builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); else builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); return true; } /// /// Determines if a given object is numeric in any way /// (can be integer, double, null, etc). /// static bool IsNumeric(object value) { if (value is sbyte) return true; if (value is byte) return true; if (value is short) return true; if (value is ushort) return true; if (value is int) return true; if (value is uint) return true; if (value is long) return true; if (value is ulong) return true; if (value is float) return true; if (value is double) return true; if (value is decimal) return true; return false; } private static IJsonSerializerStrategy _currentJsonSerializerStrategy; public static IJsonSerializerStrategy CurrentJsonSerializerStrategy { get { return _currentJsonSerializerStrategy ?? (_currentJsonSerializerStrategy = #if SIMPLE_JSON_DATACONTRACT DataContractJsonSerializerStrategy #else PocoJsonSerializerStrategy #endif ); } set { _currentJsonSerializerStrategy = value; } } private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; [EditorBrowsable(EditorBrowsableState.Advanced)] public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy { get { return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); } } #if SIMPLE_JSON_DATACONTRACT private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy { get { return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); } } #endif } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif interface IJsonSerializerStrategy { [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] bool TrySerializeNonPrimitiveObject(object input, out object output); object DeserializeObject(object value, Type type); } [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class PocoJsonSerializerStrategy : IJsonSerializerStrategy { internal IDictionary ConstructorCache; internal IDictionary> GetCache; internal IDictionary>> SetCache; internal static readonly Type[] EmptyTypes = new Type[0]; internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; private static readonly string[] Iso8601Format = new string[] { @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", @"yyyy-MM-dd\THH:mm:ss\Z", @"yyyy-MM-dd\THH:mm:ssK" }; public PocoJsonSerializerStrategy() { ConstructorCache = new ReflectionUtils.ThreadSafeDictionary(ContructorDelegateFactory); GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) { return clrPropertyName; } internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) { return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); } internal virtual IDictionary GetterValueFactory(Type type) { IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (getMethod.IsStatic || !getMethod.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal virtual IDictionary> SetterValueFactory(Type type) { IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (setMethod.IsStatic || !setMethod.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) continue; result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } return result; } public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) { return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); } [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] public virtual object DeserializeObject(object value, Type type) { if (type == null) throw new ArgumentNullException("type"); string str = value as string; if (type == typeof (Guid) && string.IsNullOrEmpty(str)) return default(Guid); if (value == null) return null; object obj = null; if (str != null) { if (str.Length != 0) // We know it can't be null now. { if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) return new Guid(str); if (type == typeof(Uri)) { bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); Uri result; if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) return result; return null; } if (type == typeof(string)) return str; return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); } else { if (type == typeof(Guid)) obj = default(Guid); else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) obj = null; else obj = str; } // Empty string case if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) return str; } else if (value is bool) return value; bool valueIsLong = value is long; bool valueIsDouble = value is double; if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) return value; if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) { obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) : value; } else { IDictionary objects = value as IDictionary; if (objects != null) { IDictionary jsonObject = objects; if (ReflectionUtils.IsTypeDictionary(type)) { // if dictionary then Type[] types = ReflectionUtils.GetGenericTypeArguments(type); Type keyType = types[0]; Type valueType = types[1]; Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); IDictionary dict = (IDictionary)ConstructorCache[genericType](); foreach (KeyValuePair kvp in jsonObject) dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); obj = dict; } else { if (type == typeof(object)) obj = value; else { obj = ConstructorCache[type](); foreach (KeyValuePair> setter in SetCache[type]) { object jsonValue; if (jsonObject.TryGetValue(setter.Key, out jsonValue)) { jsonValue = DeserializeObject(jsonValue, setter.Value.Key); setter.Value.Value(obj, jsonValue); } } } } } else { IList valueAsList = value as IList; if (valueAsList != null) { IList jsonObject = valueAsList; IList list = null; if (type.IsArray) { list = (IList)ConstructorCache[type](jsonObject.Count); int i = 0; foreach (object o in jsonObject) list[i++] = DeserializeObject(o, type.GetElementType()); } else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) { Type innerType = ReflectionUtils.GetGenericListElementType(type); list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); foreach (object o in jsonObject) list.Add(DeserializeObject(o, innerType)); } obj = list; } } return obj; } if (ReflectionUtils.IsNullableType(type)) return ReflectionUtils.ToNullableType(obj, type); return obj; } protected virtual object SerializeEnum(Enum p) { return Convert.ToDouble(p, CultureInfo.InvariantCulture); } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeKnownTypes(object input, out object output) { bool returnValue = true; if (input is DateTime) output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is DateTimeOffset) output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); else if (input is Guid) output = ((Guid)input).ToString("D"); else if (input is Uri) output = input.ToString(); else { Enum inputEnum = input as Enum; if (inputEnum != null) output = SerializeEnum(inputEnum); else { returnValue = false; output = null; } } return returnValue; } [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification="Need to support .NET 2")] protected virtual bool TrySerializeUnknownTypes(object input, out object output) { if (input == null) throw new ArgumentNullException("input"); output = null; Type type = input.GetType(); if (type.FullName == null) return false; IDictionary obj = new JsonObject(); IDictionary getters = GetCache[type]; foreach (KeyValuePair getter in getters) { if (getter.Value != null) obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); } output = obj; return true; } } #if SIMPLE_JSON_DATACONTRACT [GeneratedCode("simple-json", "1.0.0")] #if SIMPLE_JSON_INTERNAL internal #else public #endif class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy { public DataContractJsonSerializerStrategy() { GetCache = new ReflectionUtils.ThreadSafeDictionary>(GetterValueFactory); SetCache = new ReflectionUtils.ThreadSafeDictionary>>(SetterValueFactory); } internal override IDictionary GetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.GetterValueFactory(type); string jsonKey; IDictionary result = new Dictionary(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanRead) { MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); } return result; } internal override IDictionary> SetterValueFactory(Type type) { bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; if (!hasDataContract) return base.SetterValueFactory(type); string jsonKey; IDictionary> result = new Dictionary>(); foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) { if (propertyInfo.CanWrite) { MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); } } foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) { if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) result[jsonKey] = new KeyValuePair(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); } // todo implement sorting for DATACONTRACT. return result; } private static bool CanAdd(MemberInfo info, out string jsonKey) { jsonKey = null; if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) return false; DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); if (dataMemberAttribute == null) return false; jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; return true; } } #endif namespace Reflection { // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules // that might be in place in the target project. [GeneratedCode("reflection-utils", "1.0.0")] #if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC public #else internal #endif class ReflectionUtils { private static readonly object[] EmptyObjects = new object[] { }; public delegate object GetDelegate(object source); public delegate void SetDelegate(object source, object value); public delegate object ConstructorDelegate(params object[] args); public delegate TValue ThreadSafeDictionaryValueFactory(TKey key); #if SIMPLE_JSON_TYPEINFO public static TypeInfo GetTypeInfo(Type type) { return type.GetTypeInfo(); } #else public static Type GetTypeInfo(Type type) { return type; } #endif public static Attribute GetAttribute(MemberInfo info, Type type) { #if SIMPLE_JSON_TYPEINFO if (info == null || type == null || !info.IsDefined(type)) return null; return info.GetCustomAttribute(type); #else if (info == null || type == null || !Attribute.IsDefined(info, type)) return null; return Attribute.GetCustomAttribute(info, type); #endif } public static Type GetGenericListElementType(Type type) { IEnumerable interfaces; #if SIMPLE_JSON_TYPEINFO interfaces = type.GetTypeInfo().ImplementedInterfaces; #else interfaces = type.GetInterfaces(); #endif foreach (Type implementedInterface in interfaces) { if (IsTypeGeneric(implementedInterface) && implementedInterface.GetGenericTypeDefinition() == typeof (IList<>)) { return GetGenericTypeArguments(implementedInterface)[0]; } } return GetGenericTypeArguments(type)[0]; } public static Attribute GetAttribute(Type objectType, Type attributeType) { #if SIMPLE_JSON_TYPEINFO if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) return null; return objectType.GetTypeInfo().GetCustomAttribute(attributeType); #else if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) return null; return Attribute.GetCustomAttribute(objectType, attributeType); #endif } public static Type[] GetGenericTypeArguments(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().GenericTypeArguments; #else return type.GetGenericArguments(); #endif } public static bool IsTypeGeneric(Type type) { return GetTypeInfo(type).IsGenericType; } public static bool IsTypeGenericeCollectionInterface(Type type) { if (!IsTypeGeneric(type)) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>) #if SIMPLE_JSON_READONLY_COLLECTIONS || genericDefinition == typeof(IReadOnlyCollection<>) || genericDefinition == typeof(IReadOnlyList<>) #endif ); } public static bool IsAssignableFrom(Type type1, Type type2) { return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); } public static bool IsTypeDictionary(Type type) { #if SIMPLE_JSON_TYPEINFO if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) return true; #else if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) return true; #endif if (!GetTypeInfo(type).IsGenericType) return false; Type genericDefinition = type.GetGenericTypeDefinition(); return genericDefinition == typeof(IDictionary<,>); } public static bool IsNullableType(Type type) { return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } public static object ToNullableType(object obj, Type nullableType) { return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); } public static bool IsValueType(Type type) { return GetTypeInfo(type).IsValueType; } public static IEnumerable GetConstructors(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetTypeInfo().DeclaredConstructors; #else return type.GetConstructors(); #endif } public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) { IEnumerable constructorInfos = GetConstructors(type); int i; bool matches; foreach (ConstructorInfo constructorInfo in constructorInfos) { ParameterInfo[] parameters = constructorInfo.GetParameters(); if (argsType.Length != parameters.Length) continue; i = 0; matches = true; foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) { if (parameterInfo.ParameterType != argsType[i]) { matches = false; break; } } if (matches) return constructorInfo; } return null; } public static IEnumerable GetProperties(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeProperties(); #else return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static IEnumerable GetFields(Type type) { #if SIMPLE_JSON_TYPEINFO return type.GetRuntimeFields(); #else return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); #endif } public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.GetMethod; #else return propertyInfo.GetGetMethod(true); #endif } public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) { #if SIMPLE_JSON_TYPEINFO return propertyInfo.SetMethod; #else return propertyInfo.GetSetMethod(true); #endif } public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(constructorInfo); #else return GetConstructorByExpression(constructorInfo); #endif } public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetConstructorByReflection(type, argsType); #else return GetConstructorByExpression(type, argsType); #endif } public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) { return delegate(object[] args) { return constructorInfo.Invoke(args); }; } public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) { ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); Expression[] argsExp = new Expression[paramsInfo.Length]; for (int i = 0; i < paramsInfo.Length; i++) { Expression index = Expression.Constant(i); Type paramType = paramsInfo[i].ParameterType; Expression paramAccessorExp = Expression.ArrayIndex(param, index); Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); argsExp[i] = paramCastExp; } NewExpression newExp = Expression.New(constructorInfo, argsExp); Expression> lambda = Expression.Lambda>(newExp, param); Func compiledLambda = lambda.Compile(); return delegate(object[] args) { return compiledLambda(args); }; } public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) { ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); } #endif public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(propertyInfo); #else return GetGetMethodByExpression(propertyInfo); #endif } public static GetDelegate GetGetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetGetMethodByReflection(fieldInfo); #else return GetGetMethodByExpression(fieldInfo); #endif } public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); return delegate(object source) { return methodInfo.Invoke(source, EmptyObjects); }; } public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source) { return fieldInfo.GetValue(source); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); Func compiled = Expression.Lambda>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); GetDelegate compiled = Expression.Lambda(Expression.Convert(member, typeof(object)), instance).Compile(); return delegate(object source) { return compiled(source); }; } #endif public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(propertyInfo); #else return GetSetMethodByExpression(propertyInfo); #endif } public static SetDelegate GetSetMethod(FieldInfo fieldInfo) { #if SIMPLE_JSON_NO_LINQ_EXPRESSION return GetSetMethodByReflection(fieldInfo); #else return GetSetMethodByExpression(fieldInfo); #endif } public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) { MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); return delegate(object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; } public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) { return delegate(object source, object value) { fieldInfo.SetValue(source, value); }; } #if !SIMPLE_JSON_NO_LINQ_EXPRESSION public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) { MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); Action compiled = Expression.Lambda>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) { ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); ParameterExpression value = Expression.Parameter(typeof(object), "value"); Action compiled = Expression.Lambda>( Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); return delegate(object source, object val) { compiled(source, val); }; } public static BinaryExpression Assign(Expression left, Expression right) { #if SIMPLE_JSON_TYPEINFO return Expression.Assign(left, right); #else MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); BinaryExpression assignExpr = Expression.Add(left, right, assign); return assignExpr; #endif } private static class Assigner { public static T Assign(ref T left, T right) { return (left = right); } } #endif public sealed class ThreadSafeDictionary : IDictionary { private readonly object _lock = new object(); private readonly ThreadSafeDictionaryValueFactory _valueFactory; private Dictionary _dictionary; public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory valueFactory) { _valueFactory = valueFactory; } private TValue Get(TKey key) { if (_dictionary == null) return AddValue(key); TValue value; if (!_dictionary.TryGetValue(key, out value)) return AddValue(key); return value; } private TValue AddValue(TKey key) { TValue value = _valueFactory(key); lock (_lock) { if (_dictionary == null) { _dictionary = new Dictionary(); _dictionary[key] = value; } else { TValue val; if (_dictionary.TryGetValue(key, out val)) return val; Dictionary dict = new Dictionary(_dictionary); dict[key] = value; _dictionary = dict; } } return value; } public void Add(TKey key, TValue value) { throw new NotImplementedException(); } public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); } public ICollection Keys { get { return _dictionary.Keys; } } public bool Remove(TKey key) { throw new NotImplementedException(); } public bool TryGetValue(TKey key, out TValue value) { value = this[key]; return true; } public ICollection Values { get { return _dictionary.Values; } } public TValue this[TKey key] { get { return Get(key); } set { throw new NotImplementedException(); } } public void Add(KeyValuePair item) { throw new NotImplementedException(); } public void Clear() { throw new NotImplementedException(); } public bool Contains(KeyValuePair item) { throw new NotImplementedException(); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { throw new NotImplementedException(); } public int Count { get { return _dictionary.Count; } } public bool IsReadOnly { get { throw new NotImplementedException(); } } public bool Remove(KeyValuePair item) { throw new NotImplementedException(); } public IEnumerator> GetEnumerator() { return _dictionary.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); } } } } } // ReSharper restore LoopCanBeConvertedToQuery // ReSharper restore RedundantExplicitArrayCreation // ReSharper restore SuggestUseVarKeywordEvident ================================================ FILE: src/Nancy/Jsonp.cs ================================================ namespace Nancy { using System; using System.IO; using System.Linq; using System.Text; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Json; /// /// Handles JSONP requests. /// public static class Jsonp { private static readonly PipelineItem> JsonpItem; private static Encoding encoding; static Jsonp() { JsonpItem = new PipelineItem>("JSONP", PrepareJsonp); } private static string Encoding { get { return string.Concat("; charset=", encoding.WebName); } } /// /// Enable JSONP support in the application /// /// Application Pipeline to Hook into /// An instance. public static void Enable(IPipelines pipelines, INancyEnvironment environment) { var jsonpEnabled = pipelines.AfterRequest.PipelineItems.Any(ctx => ctx.Name == "JSONP"); if (!jsonpEnabled) { encoding = environment.GetValue().DefaultEncoding; pipelines.AfterRequest.AddItemToEndOfPipeline(JsonpItem); } } /// /// Disable JSONP support in the application /// /// Application Pipeline to Hook into public static void Disable(IPipelines pipelines) { pipelines.AfterRequest.RemoveByName("JSONP"); } /// /// Transmogrify original response and apply JSONP Padding /// /// Current Nancy Context private static void PrepareJsonp(NancyContext context) { var isJson = Json.Json.IsJsonContentType(context.Response.ContentType); bool hasCallback = context.Request.Query["callback"].HasValue; if (!isJson || !hasCallback) { return; } // grab original contents for running later var original = context.Response.Contents; string callback = context.Request.Query["callback"].Value; // set content type to application/javascript so browsers can handle it by default // http://stackoverflow.com/questions/111302/best-content-type-to-serve-jsonp context.Response.ContentType = string.Concat("application/javascript", Encoding); context.Response.Contents = stream => { // disposing of stream is handled elsewhere var writer = new StreamWriter(stream) { AutoFlush = true }; writer.Write("{0}(", callback); original(stream); writer.Write(");"); }; } } } ================================================ FILE: src/Nancy/JsonpApplicationStartup.cs ================================================ namespace Nancy { using Nancy.Bootstrapper; using Nancy.Configuration; /// /// Enables JSONP support at application startup. /// public class JsonpApplicationStartup : IApplicationStartup { private readonly INancyEnvironment environment; /// /// Initializes a new instance of the class, /// with the provided instance. /// /// An instance. public JsonpApplicationStartup(INancyEnvironment environment) { this.environment = environment; } /// /// Perform any initialisation tasks /// /// Application pipelines public void Initialize(IPipelines pipelines) { Jsonp.Enable(pipelines, this.environment); } } } ================================================ FILE: src/Nancy/Localization/ITextResource.cs ================================================ namespace Nancy.Localization { /// /// Used to return string values /// public interface ITextResource { /// /// Gets a translation based on the provided key. /// /// The key to look up the translation for. /// The current instance. string this[string key, NancyContext context] { get; } } } ================================================ FILE: src/Nancy/Localization/ResourceBasedTextResource.cs ================================================ namespace Nancy.Localization { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Resources; /// /// Resource based implementation of /// public class ResourceBasedTextResource : ITextResource { private readonly IResourceAssemblyProvider resourceAssemblyProvider; private readonly IDictionary resourceManagers; /// /// Initializes a new instance of to read strings from *.resx files /// /// The that should be used when scanning. public ResourceBasedTextResource(IResourceAssemblyProvider resourceAssemblyProvider) { this.resourceAssemblyProvider = resourceAssemblyProvider; var resources = from assembly in this.resourceAssemblyProvider.GetAssembliesToScan() from resourceName in assembly.GetManifestResourceNames() where resourceName.EndsWith(".resources") let name = Path.GetFileNameWithoutExtension(resourceName) let baseName = resourceName.Replace(".resources", string.Empty) select new { Name = name, Manager = new ResourceManager(baseName, assembly) }; this.resourceManagers = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var x in resources) { if (!this.resourceManagers.ContainsKey(x.Name)) { this.resourceManagers[x.Name] = x.Manager; } else { throw new ArgumentException(string.Format("Key '{0}' already exists;",x.Name)); } } } /// /// Used to return a string value from *.resx files /// /// The key to look for in the resource file /// The used to determine the culture for returning culture specific values. /// Returns a string value from culture specific or default file or null if key does not exist as determined by . public string this[string key, NancyContext context] { get { var components = GetKeyComponents(key); var candidates = this.resourceManagers.Where( x => x.Key.EndsWith("." + components.Item1, StringComparison.OrdinalIgnoreCase)).ToArray(); if (candidates.Count() > 1) { throw new InvalidOperationException("More than one text resources match the " + components.Item1 + " key. Try providing a more specific key."); } var manager = candidates.Any() ? candidates.First().Value : null; return (manager == null) ? null : manager.GetString(components.Item2, context.Culture); } } private static Tuple GetKeyComponents(string key) { var index = key.LastIndexOf(".", StringComparison.Ordinal); if (index == -1) { throw new InvalidOperationException("The text key needs to be specified in the format resourcename.resourcekey, where resourcename should at least be the name of the resource file and at most the fully qualified path."); } return new Tuple( key.Substring(0, index), key.Substring(index + 1)); } } } ================================================ FILE: src/Nancy/Localization/TextResourceFinder.cs ================================================ namespace Nancy.Localization { using System; using System.Dynamic; /// /// Returns text from an implemented ITextResource /// public class TextResourceFinder : DynamicObject { private readonly ITextResource textResource; private readonly NancyContext context; /// /// Initializes a new instance of the class, with /// the provided and . /// /// The that should be used by the TextResourceFinder /// The that should be used by the TextResourceFinder public TextResourceFinder(ITextResource textResource, NancyContext context) { this.textResource = textResource; this.context = context; } /// /// Gets the that is being used to locate texts. /// /// An instance. public ITextResource Resource { get { return this.textResource; } } /// /// Finds text resource /// /// GetMemberBinder with dynamic text key /// Text item /// Returns a value or a non existing value from the implementation public override bool TryGetMember(GetMemberBinder binder, out object result) { result = new DynamicMemberChainer(binder.Name, this.context, this.textResource); return true; } /// /// Gets a translation based on the provided key. /// /// The key to look up the translation for. public string this[string key] { get { return this.textResource[key, this.context]; } } /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// public class DynamicMemberChainer : DynamicObject { private string memberName; private readonly NancyContext context; private readonly ITextResource textResource; /// /// Initializes a new instance of the class. /// /// Name of the member. /// The nancy context instance. /// The text resource instance. public DynamicMemberChainer(string memberName, NancyContext context, ITextResource resource) { this.memberName = memberName; this.context = context; this.textResource = resource; } /// /// Gets the member name concatenated to binder name. /// /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.) /// public override bool TryGetMember(GetMemberBinder binder, out object result) { this.memberName = string.Concat(this.memberName, ".", binder.Name); result = this; return true; } /// /// Attempts to convert provided member name and context to the text resource representation. /// /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. /// The result of the type conversion operation. /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// /// Cannot cast dynamic member access to anything else than a string. public override bool TryConvert(ConvertBinder binder, out object result) { if (binder.ReturnType == typeof(string)) { result = this.textResource[this.memberName, this.context]; return true; } throw new InvalidOperationException("Cannot cast dynamic member access to anything else than a string."); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this.textResource[this.memberName, this.context]; } } } } ================================================ FILE: src/Nancy/MimeTypes.cs ================================================ // // Nancy.MimeTypes // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // (C) 2002 Ximian, Inc (http://www.ximian.com) // (C) 2003-2009 Novell, Inc (http://novell.com) // // 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. // namespace Nancy { using System; using System.Collections.Generic; /// /// Holds the MIME types /// public sealed class MimeTypes { static Dictionary mimeTypes; static MimeTypes () { mimeTypes = new Dictionary (StringComparer.OrdinalIgnoreCase); mimeTypes.Add ("323", "text/h323"); mimeTypes.Add ("3dmf", "x-world/x-3dmf"); mimeTypes.Add ("3dm", "x-world/x-3dmf"); mimeTypes.Add ("7z", "application/x-7z-compressed"); mimeTypes.Add ("aab", "application/x-authorware-bin"); mimeTypes.Add ("aam", "application/x-authorware-map"); mimeTypes.Add ("aas", "application/x-authorware-seg"); mimeTypes.Add ("abc", "text/vnd.abc"); mimeTypes.Add ("acgi", "text/html"); mimeTypes.Add ("acx", "application/internet-property-stream"); mimeTypes.Add ("afl", "video/animaflex"); mimeTypes.Add ("ai", "application/postscript"); mimeTypes.Add ("aif", "audio/aiff"); mimeTypes.Add ("aifc", "audio/aiff"); mimeTypes.Add ("aiff", "audio/aiff"); mimeTypes.Add ("aim", "application/x-aim"); mimeTypes.Add ("aip", "text/x-audiosoft-intra"); mimeTypes.Add ("ani", "application/x-navi-animation"); mimeTypes.Add ("aos", "application/x-nokia-9000-communicator-add-on-software"); mimeTypes.Add ("application", "application/x-ms-application"); mimeTypes.Add ("aps", "application/mime"); mimeTypes.Add ("art", "image/x-jg"); mimeTypes.Add ("asf", "video/x-ms-asf"); mimeTypes.Add ("asm", "text/x-asm"); mimeTypes.Add ("asp", "text/asp"); mimeTypes.Add ("asr", "video/x-ms-asf"); mimeTypes.Add ("asx", "application/x-mplayer2"); mimeTypes.Add ("atom", "application/atom.xml"); mimeTypes.Add ("atomcat", "application/atomcat+xml"); mimeTypes.Add ("atomsvc", "application/atomsvc+xml"); mimeTypes.Add ("au", "audio/x-au"); mimeTypes.Add ("avi", "video/avi"); mimeTypes.Add ("avs", "video/avs-video"); mimeTypes.Add ("axs", "application/olescript"); mimeTypes.Add ("bas", "text/plain"); mimeTypes.Add ("bcpio", "application/x-bcpio"); mimeTypes.Add ("bin", "application/octet-stream"); mimeTypes.Add ("bm", "image/bmp"); mimeTypes.Add ("bmp", "image/bmp"); mimeTypes.Add ("boo", "application/book"); mimeTypes.Add ("book", "application/book"); mimeTypes.Add ("boz", "application/x-bzip2"); mimeTypes.Add ("bsh", "application/x-bsh"); mimeTypes.Add ("bz2", "application/x-bzip2"); mimeTypes.Add ("bz", "application/x-bzip"); mimeTypes.Add ("cat", "application/vnd.ms-pki.seccat"); mimeTypes.Add ("ccad", "application/clariscad"); mimeTypes.Add ("cco", "application/x-cocoa"); mimeTypes.Add ("cc", "text/plain"); mimeTypes.Add ("cdf", "application/cdf"); mimeTypes.Add ("cer", "application/pkix-cert"); mimeTypes.Add ("cha", "application/x-chat"); mimeTypes.Add ("chat", "application/x-chat"); mimeTypes.Add ("class", "application/java"); mimeTypes.Add ("clp", "application/x-msclip"); mimeTypes.Add ("cmx", "image/x-cmx"); mimeTypes.Add ("cod", "image/cis-cod"); mimeTypes.Add ("conf", "text/plain"); mimeTypes.Add ("cpio", "application/x-cpio"); mimeTypes.Add ("cpp", "text/plain"); mimeTypes.Add ("cpt", "application/x-cpt"); mimeTypes.Add ("crd", "application/x-mscardfile"); mimeTypes.Add ("crl", "application/pkix-crl"); mimeTypes.Add ("crt", "application/pkix-cert"); mimeTypes.Add ("csh", "application/x-csh"); mimeTypes.Add ("css", "text/css"); mimeTypes.Add ("c", "text/plain"); mimeTypes.Add ("c++", "text/plain"); mimeTypes.Add ("cs", "text/plain"); mimeTypes.Add ("cxx", "text/plain"); mimeTypes.Add ("dcr", "application/x-director"); mimeTypes.Add ("deepv", "application/x-deepv"); mimeTypes.Add ("def", "text/plain"); mimeTypes.Add ("deploy", "application/octet-stream"); mimeTypes.Add ("der", "application/x-x509-ca-cert"); mimeTypes.Add ("dib", "image/bmp"); mimeTypes.Add ("dif", "video/x-dv"); mimeTypes.Add ("dir", "application/x-director"); mimeTypes.Add ("disco", "application/xml"); mimeTypes.Add ("dll", "application/x-msdownload"); mimeTypes.Add ("dl", "video/dl"); mimeTypes.Add ("doc", "application/msword"); mimeTypes.Add ("dot", "application/msword"); mimeTypes.Add ("dp", "application/commonground"); mimeTypes.Add ("drw", "application/drafting"); mimeTypes.Add ("dvi", "application/x-dvi"); mimeTypes.Add ("dv", "video/x-dv"); mimeTypes.Add ("dwf", "drawing/x-dwf (old)"); mimeTypes.Add ("dwg", "application/acad"); mimeTypes.Add ("dxf", "application/dxf"); mimeTypes.Add ("dxr", "application/x-director"); mimeTypes.Add ("elc", "application/x-elc"); mimeTypes.Add ("el", "text/x-script.elisp"); mimeTypes.Add ("eml", "message/rfc822"); mimeTypes.Add ("eot", "application/vnd.bw-fontobject"); mimeTypes.Add ("eps", "application/postscript"); mimeTypes.Add ("es", "application/x-esrehber"); mimeTypes.Add ("etx", "text/x-setext"); mimeTypes.Add ("evy", "application/envoy"); mimeTypes.Add ("exe", "application/octet-stream"); mimeTypes.Add ("f77", "text/plain"); mimeTypes.Add ("f90", "text/plain"); mimeTypes.Add ("fdf", "application/vnd.fdf"); mimeTypes.Add ("fif", "image/fif"); mimeTypes.Add ("fli", "video/fli"); mimeTypes.Add ("flo", "image/florian"); mimeTypes.Add ("flr", "x-world/x-vrml"); mimeTypes.Add ("flx", "text/vnd.fmi.flexstor"); mimeTypes.Add ("fmf", "video/x-atomic3d-feature"); mimeTypes.Add ("for", "text/plain"); mimeTypes.Add ("fpx", "image/vnd.fpx"); mimeTypes.Add ("frl", "application/freeloader"); mimeTypes.Add ("f", "text/plain"); mimeTypes.Add ("funk", "audio/make"); mimeTypes.Add ("g3", "image/g3fax"); mimeTypes.Add ("gadget", "application/x-windows-gadget"); mimeTypes.Add ("gif", "image/gif"); mimeTypes.Add ("gl", "video/gl"); mimeTypes.Add ("gsd", "audio/x-gsm"); mimeTypes.Add ("gsm", "audio/x-gsm"); mimeTypes.Add ("gsp", "application/x-gsp"); mimeTypes.Add ("gss", "application/x-gss"); mimeTypes.Add ("gtar", "application/x-gtar"); mimeTypes.Add ("g", "text/plain"); mimeTypes.Add ("gz", "application/x-gzip"); mimeTypes.Add ("gzip", "application/x-gzip"); mimeTypes.Add ("hdf", "application/x-hdf"); mimeTypes.Add ("help", "application/x-helpfile"); mimeTypes.Add ("hgl", "application/vnd.hp-HPGL"); mimeTypes.Add ("hh", "text/plain"); mimeTypes.Add ("hlb", "text/x-script"); mimeTypes.Add ("hlp", "application/x-helpfile"); mimeTypes.Add ("hpg", "application/vnd.hp-HPGL"); mimeTypes.Add ("hpgl", "application/vnd.hp-HPGL"); mimeTypes.Add ("hqx", "application/binhex"); mimeTypes.Add ("hta", "application/hta"); mimeTypes.Add ("htc", "text/x-component"); mimeTypes.Add ("h", "text/plain"); mimeTypes.Add ("htmls", "text/html"); mimeTypes.Add ("html", "text/html"); mimeTypes.Add ("htm", "text/html"); mimeTypes.Add ("htt", "text/webviewhtml"); mimeTypes.Add ("htx", "text/html"); mimeTypes.Add ("ice", "x-conference/x-cooltalk"); mimeTypes.Add ("ico", "image/x-icon"); mimeTypes.Add ("idc", "text/plain"); mimeTypes.Add ("ief", "image/ief"); mimeTypes.Add ("iefs", "image/ief"); mimeTypes.Add ("iges", "application/iges"); mimeTypes.Add ("igs", "application/iges"); mimeTypes.Add ("iii", "application/x-iphone"); mimeTypes.Add ("ima", "application/x-ima"); mimeTypes.Add ("imap", "application/x-httpd-imap"); mimeTypes.Add ("inf", "application/inf"); mimeTypes.Add ("ins", "application/x-internett-signup"); mimeTypes.Add ("ip", "application/x-ip2"); mimeTypes.Add ("isp", "application/x-internet-signup"); mimeTypes.Add ("isu", "video/x-isvideo"); mimeTypes.Add ("it", "audio/it"); mimeTypes.Add ("iv", "application/x-inventor"); mimeTypes.Add ("ivf", "video/x-ivf"); mimeTypes.Add ("ivr", "i-world/i-vrml"); mimeTypes.Add ("ivy", "application/x-livescreen"); mimeTypes.Add ("jam", "audio/x-jam"); mimeTypes.Add ("java", "text/plain"); mimeTypes.Add ("jav", "text/plain"); mimeTypes.Add ("jcm", "application/x-java-commerce"); mimeTypes.Add ("jfif", "image/jpeg"); mimeTypes.Add ("jfif-tbnl", "image/jpeg"); mimeTypes.Add ("jpeg", "image/jpeg"); mimeTypes.Add ("jpe", "image/jpeg"); mimeTypes.Add ("jpg", "image/jpeg"); mimeTypes.Add ("jps", "image/x-jps"); mimeTypes.Add ("js", "application/javascript"); mimeTypes.Add ("json", "application/json"); mimeTypes.Add ("jut", "image/jutvision"); mimeTypes.Add ("kar", "audio/midi"); mimeTypes.Add ("ksh", "text/x-script.ksh"); mimeTypes.Add ("la", "audio/nspaudio"); mimeTypes.Add ("lam", "audio/x-liveaudio"); mimeTypes.Add ("latex", "application/x-latex"); mimeTypes.Add ("less", "text/css"); mimeTypes.Add ("list", "text/plain"); mimeTypes.Add ("lma", "audio/nspaudio"); mimeTypes.Add ("log", "text/plain"); mimeTypes.Add ("lsp", "application/x-lisp"); mimeTypes.Add ("lst", "text/plain"); mimeTypes.Add ("lsx", "text/x-la-asf"); mimeTypes.Add ("ltx", "application/x-latex"); mimeTypes.Add ("m13", "application/x-msmediaview"); mimeTypes.Add ("m14", "application/x-msmediaview"); mimeTypes.Add ("m1v", "video/mpeg"); mimeTypes.Add ("m2a", "audio/mpeg"); mimeTypes.Add ("m2v", "video/mpeg"); mimeTypes.Add ("m3u", "audio/x-mpequrl"); mimeTypes.Add ("m4u", "video/x-mpegurl"); mimeTypes.Add ("m4v", "video/mp4"); mimeTypes.Add ("m4a", "audio/mp4"); mimeTypes.Add ("m4r", "audio/mp4"); mimeTypes.Add ("m4b", "audio/mp4"); mimeTypes.Add ("m4p", "audio/mp4"); mimeTypes.Add ("man", "application/x-troff-man"); mimeTypes.Add ("manifest", "application/x-ms-manifest"); mimeTypes.Add ("map", "application/x-navimap"); mimeTypes.Add ("mar", "text/plain"); mimeTypes.Add ("mbd", "application/mbedlet"); mimeTypes.Add ("mc$", "application/x-magic-cap-package-1.0"); mimeTypes.Add ("mcd", "application/mcad"); mimeTypes.Add ("mcf", "image/vasa"); mimeTypes.Add ("mcp", "application/netmc"); mimeTypes.Add ("mdb", "application/x-msaccess"); mimeTypes.Add ("me", "application/x-troff-me"); mimeTypes.Add ("mht", "message/rfc822"); mimeTypes.Add ("mhtml", "message/rfc822"); mimeTypes.Add ("mid", "audio/midi"); mimeTypes.Add ("midi", "audio/midi"); mimeTypes.Add ("mif", "application/x-mif"); mimeTypes.Add ("mime", "message/rfc822"); mimeTypes.Add ("mjf", "audio/x-vnd.AudioExplosion.MjuiceMediaFile"); mimeTypes.Add ("mjpg", "video/x-motion-jpeg"); mimeTypes.Add ("mm", "application/base64"); mimeTypes.Add ("mme", "application/base64"); mimeTypes.Add ("mny", "application/x-msmoney"); mimeTypes.Add ("mod", "audio/mod"); mimeTypes.Add ("moov", "video/quicktime"); mimeTypes.Add ("movie", "video/x-sgi-movie"); mimeTypes.Add ("mov", "video/quicktime"); mimeTypes.Add ("mp2", "video/mpeg"); mimeTypes.Add ("mp3", "audio/mpeg"); mimeTypes.Add ("mp4", "video/mp4"); mimeTypes.Add ("mp4v", "video/mp4"); mimeTypes.Add ("mpa", "audio/mpeg"); mimeTypes.Add ("mpc", "application/x-project"); mimeTypes.Add ("mpeg", "video/mpeg"); mimeTypes.Add ("mpe", "video/mpeg"); mimeTypes.Add ("mpga", "audio/mpeg"); mimeTypes.Add ("mpg", "video/mpeg"); mimeTypes.Add ("mpg4", "video/mp4"); mimeTypes.Add ("mpp", "application/vnd.ms-project"); mimeTypes.Add ("mpt", "application/x-project"); mimeTypes.Add ("mpv2", "video/mpeg"); mimeTypes.Add ("mpv", "application/x-project"); mimeTypes.Add ("mpx", "application/x-project"); mimeTypes.Add ("mrc", "application/marc"); mimeTypes.Add ("ms", "application/x-troff-ms"); mimeTypes.Add ("m", "text/plain"); mimeTypes.Add ("mvb", "application/x-msmediaview"); mimeTypes.Add ("mv", "video/x-sgi-movie"); mimeTypes.Add ("my", "audio/make"); mimeTypes.Add ("mzz", "application/x-vnd.AudioExplosion.mzz"); mimeTypes.Add ("nap", "image/naplps"); mimeTypes.Add ("naplps", "image/naplps"); mimeTypes.Add ("nc", "application/x-netcdf"); mimeTypes.Add ("ncm", "application/vnd.nokia.configuration-message"); mimeTypes.Add ("niff", "image/x-niff"); mimeTypes.Add ("nif", "image/x-niff"); mimeTypes.Add ("nix", "application/x-mix-transfer"); mimeTypes.Add ("nsc", "application/x-conference"); mimeTypes.Add ("nvd", "application/x-navidoc"); mimeTypes.Add ("nws", "message/rfc822"); mimeTypes.Add ("oda", "application/oda"); mimeTypes.Add ("ods", "application/oleobject"); mimeTypes.Add ("oga", "audio/ogg"); mimeTypes.Add ("ogg", "audio/ogg"); mimeTypes.Add ("ogv", "video/ogg"); mimeTypes.Add ("omc", "application/x-omc"); mimeTypes.Add ("omcd", "application/x-omcdatamaker"); mimeTypes.Add ("omcr", "application/x-omcregerator"); mimeTypes.Add ("otf", "application/x-font-otf"); mimeTypes.Add ("p10", "application/pkcs10"); mimeTypes.Add ("p12", "application/pkcs-12"); mimeTypes.Add ("p7a", "application/x-pkcs7-signature"); mimeTypes.Add ("p7b", "application/x-pkcs7-certificates"); mimeTypes.Add ("p7c", "application/pkcs7-mime"); mimeTypes.Add ("p7m", "application/pkcs7-mime"); mimeTypes.Add ("p7r", "application/x-pkcs7-certreqresp"); mimeTypes.Add ("p7s", "application/pkcs7-signature"); mimeTypes.Add ("part", "application/pro_eng"); mimeTypes.Add ("pas", "text/pascal"); mimeTypes.Add ("pbm", "image/x-portable-bitmap"); mimeTypes.Add ("pcl", "application/x-pcl"); mimeTypes.Add ("pct", "image/x-pict"); mimeTypes.Add ("pcx", "image/x-pcx"); mimeTypes.Add ("pdb", "chemical/x-pdb"); mimeTypes.Add ("pdf", "application/pdf"); mimeTypes.Add ("pfunk", "audio/make"); mimeTypes.Add ("pfx", "application/x-pkcs12"); mimeTypes.Add ("pgm", "image/x-portable-graymap"); mimeTypes.Add ("pic", "image/pict"); mimeTypes.Add ("pict", "image/pict"); mimeTypes.Add ("pkg", "application/x-newton-compatible-pkg"); mimeTypes.Add ("pko", "application/vnd.ms-pki.pko"); mimeTypes.Add ("pl", "text/plain"); mimeTypes.Add ("plx", "application/x-PiXCLscript"); mimeTypes.Add ("pm4", "application/x-pagemaker"); mimeTypes.Add ("pm5", "application/x-pagemaker"); mimeTypes.Add ("pma", "application/x-perfmon"); mimeTypes.Add ("pmc", "application/x-perfmon"); mimeTypes.Add ("pm", "image/x-xpixmap"); mimeTypes.Add ("pml", "application/x-perfmon"); mimeTypes.Add ("pmr", "application/x-perfmon"); mimeTypes.Add ("pmw", "application/x-perfmon"); mimeTypes.Add ("png", "image/png"); mimeTypes.Add ("pnm", "application/x-portable-anymap"); mimeTypes.Add ("pot", "application/mspowerpoint"); mimeTypes.Add ("pov", "model/x-pov"); mimeTypes.Add ("ppa", "application/vnd.ms-powerpoint"); mimeTypes.Add ("ppm", "image/x-portable-pixmap"); mimeTypes.Add ("pps", "application/mspowerpoint"); mimeTypes.Add ("ppt", "application/mspowerpoint"); mimeTypes.Add ("ppz", "application/mspowerpoint"); mimeTypes.Add ("pre", "application/x-freelance"); mimeTypes.Add ("prf", "application/pics-rules"); mimeTypes.Add ("prt", "application/pro_eng"); mimeTypes.Add ("ps", "application/postscript"); mimeTypes.Add ("p", "text/x-pascal"); mimeTypes.Add ("pub", "application/x-mspublisher"); mimeTypes.Add ("pvu", "paleovu/x-pv"); mimeTypes.Add ("pwz", "application/vnd.ms-powerpoint"); mimeTypes.Add ("pyc", "applicaiton/x-bytecode.python"); mimeTypes.Add ("py", "text/x-script.phyton"); mimeTypes.Add ("qcp", "audio/vnd.qcelp"); mimeTypes.Add ("qd3d", "x-world/x-3dmf"); mimeTypes.Add ("qd3", "x-world/x-3dmf"); mimeTypes.Add ("qif", "image/x-quicktime"); mimeTypes.Add ("qtc", "video/x-qtc"); mimeTypes.Add ("qtif", "image/x-quicktime"); mimeTypes.Add ("qti", "image/x-quicktime"); mimeTypes.Add ("qt", "video/quicktime"); mimeTypes.Add ("ra", "audio/x-pn-realaudio"); mimeTypes.Add ("ram", "audio/x-pn-realaudio"); mimeTypes.Add ("ras", "application/x-cmu-raster"); mimeTypes.Add ("rast", "image/cmu-raster"); mimeTypes.Add ("rexx", "text/x-script.rexx"); mimeTypes.Add ("rf", "image/vnd.rn-realflash"); mimeTypes.Add ("rgb", "image/x-rgb"); mimeTypes.Add ("rm", "application/vnd.rn-realmedia"); mimeTypes.Add ("rmi", "audio/mid"); mimeTypes.Add ("rmm", "audio/x-pn-realaudio"); mimeTypes.Add ("rmp", "audio/x-pn-realaudio"); mimeTypes.Add ("rng", "application/ringing-tones"); mimeTypes.Add ("rnx", "application/vnd.rn-realplayer"); mimeTypes.Add ("roff", "application/x-troff"); mimeTypes.Add ("rp", "image/vnd.rn-realpix"); mimeTypes.Add ("rpm", "audio/x-pn-realaudio-plugin"); mimeTypes.Add ("rss", "application/xml"); mimeTypes.Add ("rtf", "text/richtext"); mimeTypes.Add ("rt", "text/richtext"); mimeTypes.Add ("rtx", "text/richtext"); mimeTypes.Add ("rv", "video/vnd.rn-realvideo"); mimeTypes.Add ("s3m", "audio/s3m"); mimeTypes.Add ("sbk", "application/x-tbook"); mimeTypes.Add ("scd", "application/x-msschedule"); mimeTypes.Add ("scm", "application/x-lotusscreencam"); mimeTypes.Add ("sct", "text/scriptlet"); mimeTypes.Add ("sdml", "text/plain"); mimeTypes.Add ("sdp", "application/sdp"); mimeTypes.Add ("sdr", "application/sounder"); mimeTypes.Add ("sea", "application/sea"); mimeTypes.Add ("set", "application/set"); mimeTypes.Add ("setpay", "application/set-payment-initiation"); mimeTypes.Add ("setreg", "application/set-registration-initiation"); mimeTypes.Add ("sgml", "text/sgml"); mimeTypes.Add ("sgm", "text/sgml"); mimeTypes.Add ("shar", "application/x-bsh"); mimeTypes.Add ("sh", "text/x-script.sh"); mimeTypes.Add ("shtml", "text/html"); mimeTypes.Add ("sid", "audio/x-psid"); mimeTypes.Add ("sit", "application/x-sit"); mimeTypes.Add ("skd", "application/x-koan"); mimeTypes.Add ("skm", "application/x-koan"); mimeTypes.Add ("skp", "application/x-koan"); mimeTypes.Add ("skt", "application/x-koan"); mimeTypes.Add ("sl", "application/x-seelogo"); mimeTypes.Add ("smi", "application/smil"); mimeTypes.Add ("smil", "application/smil"); mimeTypes.Add ("snd", "audio/basic"); mimeTypes.Add ("sol", "application/solids"); mimeTypes.Add ("spc", "application/x-pkcs7-certificates"); mimeTypes.Add ("spl", "application/futuresplash"); mimeTypes.Add ("spr", "application/x-sprite"); mimeTypes.Add ("sprite", "application/x-sprite"); mimeTypes.Add ("spx", "audio/ogg"); mimeTypes.Add ("src", "application/x-wais-source"); mimeTypes.Add ("ssi", "text/x-server-parsed-html"); mimeTypes.Add ("ssm", "application/streamingmedia"); mimeTypes.Add ("sst", "application/vnd.ms-pki.certstore"); mimeTypes.Add ("step", "application/step"); mimeTypes.Add ("s", "text/x-asm"); mimeTypes.Add ("stl", "application/sla"); mimeTypes.Add ("stm", "text/html"); mimeTypes.Add ("stp", "application/step"); mimeTypes.Add ("sv4cpio", "application/x-sv4cpio"); mimeTypes.Add ("sv4crc", "application/x-sv4crc"); mimeTypes.Add ("svf", "image/x-dwg"); mimeTypes.Add ("svg", "image/svg+xml"); mimeTypes.Add ("svgz", "image/svg+xml"); mimeTypes.Add ("svr", "application/x-world"); mimeTypes.Add ("swf", "application/x-shockwave-flash"); mimeTypes.Add ("talk", "text/x-speech"); mimeTypes.Add ("t", "application/x-troff"); mimeTypes.Add ("tar", "application/x-tar"); mimeTypes.Add ("tbk", "application/toolbook"); mimeTypes.Add ("tcl", "text/x-script.tcl"); mimeTypes.Add ("tcsh", "text/x-script.tcsh"); mimeTypes.Add ("tex", "application/x-tex"); mimeTypes.Add ("texi", "application/x-texinfo"); mimeTypes.Add ("texinfo", "application/x-texinfo"); mimeTypes.Add ("text", "text/plain"); mimeTypes.Add ("tgz", "application/x-compressed"); mimeTypes.Add ("tiff", "image/tiff"); mimeTypes.Add ("tif", "image/tiff"); mimeTypes.Add ("torrent", "application/x-bittorrent"); mimeTypes.Add ("tr", "application/x-troff"); mimeTypes.Add ("trm", "application/x-msterminal"); mimeTypes.Add ("tsi", "audio/tsp-audio"); mimeTypes.Add ("tsp", "audio/tsplayer"); mimeTypes.Add ("tsv", "text/tab-separated-values"); mimeTypes.Add ("ttf", "application/x-font-ttf"); mimeTypes.Add ("turbot", "image/florian"); mimeTypes.Add ("txt", "text/plain"); mimeTypes.Add ("uil", "text/x-uil"); mimeTypes.Add ("uls", "text/iuls"); mimeTypes.Add ("unis", "text/uri-list"); mimeTypes.Add ("uni", "text/uri-list"); mimeTypes.Add ("unv", "application/i-deas"); mimeTypes.Add ("uris", "text/uri-list"); mimeTypes.Add ("uri", "text/uri-list"); mimeTypes.Add ("ustar", "multipart/x-ustar"); mimeTypes.Add ("uue", "text/x-uuencode"); mimeTypes.Add ("uu", "text/x-uuencode"); mimeTypes.Add ("vcd", "application/x-cdlink"); mimeTypes.Add ("vcf", "text/x-vcard"); mimeTypes.Add ("vcs", "text/x-vCalendar"); mimeTypes.Add ("vda", "application/vda"); mimeTypes.Add ("vdo", "video/vdo"); mimeTypes.Add ("vew", "application/groupwise"); mimeTypes.Add ("vivo", "video/vivo"); mimeTypes.Add ("viv", "video/vivo"); mimeTypes.Add ("vmd", "application/vocaltec-media-desc"); mimeTypes.Add ("vmf", "application/vocaltec-media-file"); mimeTypes.Add ("voc", "audio/voc"); mimeTypes.Add ("vos", "video/vosaic"); mimeTypes.Add ("vox", "audio/voxware"); mimeTypes.Add ("vqe", "audio/x-twinvq-plugin"); mimeTypes.Add ("vqf", "audio/x-twinvq"); mimeTypes.Add ("vql", "audio/x-twinvq-plugin"); mimeTypes.Add ("vrml", "application/x-vrml"); mimeTypes.Add ("vrt", "x-world/x-vrt"); mimeTypes.Add ("vsd", "application/x-visio"); mimeTypes.Add ("vst", "application/x-visio"); mimeTypes.Add ("vsw", "application/x-visio"); mimeTypes.Add ("w60", "application/wordperfect6.0"); mimeTypes.Add ("w61", "application/wordperfect6.1"); mimeTypes.Add ("w6w", "application/msword"); mimeTypes.Add ("wav", "audio/wav"); mimeTypes.Add ("wb1", "application/x-qpro"); mimeTypes.Add ("wbmp", "image/vnd.wap.wbmp"); mimeTypes.Add ("wcm", "application/vnd.ms-works"); mimeTypes.Add ("wdb", "application/vnd.ms-works"); mimeTypes.Add ("web", "application/vnd.xara"); mimeTypes.Add ("webm", "video/webm"); mimeTypes.Add ("wiz", "application/msword"); mimeTypes.Add ("wk1", "application/x-123"); mimeTypes.Add ("wks", "application/vnd.ms-works"); mimeTypes.Add ("wmf", "windows/metafile"); mimeTypes.Add ("wmlc", "application/vnd.wap.wmlc"); mimeTypes.Add ("wmlsc", "application/vnd.wap.wmlscriptc"); mimeTypes.Add ("wmls", "text/vnd.wap.wmlscript"); mimeTypes.Add ("wml", "text/vnd.wap.wml"); mimeTypes.Add ("woff", "application/font-woff"); mimeTypes.Add ("word", "application/msword"); mimeTypes.Add ("wp5", "application/wordperfect"); mimeTypes.Add ("wp6", "application/wordperfect"); mimeTypes.Add ("wp", "application/wordperfect"); mimeTypes.Add ("wpd", "application/wordperfect"); mimeTypes.Add ("wps", "application/vnd.ms-works"); mimeTypes.Add ("wq1", "application/x-lotus"); mimeTypes.Add ("wri", "application/mswrite"); mimeTypes.Add ("wrl", "application/x-world"); mimeTypes.Add ("wrz", "model/vrml"); mimeTypes.Add ("wsc", "text/scriplet"); mimeTypes.Add ("wsdl", "application/xml"); mimeTypes.Add ("wsrc", "application/x-wais-source"); mimeTypes.Add ("wtk", "application/x-wintalk"); mimeTypes.Add ("xaf", "x-world/x-vrml"); mimeTypes.Add ("xaml", "application/xaml+xml"); mimeTypes.Add ("xap", "application/x-silverlight-app"); mimeTypes.Add ("xbap", "application/x-ms-xbap"); mimeTypes.Add ("xbm", "image/x-xbitmap"); mimeTypes.Add ("xdr", "video/x-amt-demorun"); mimeTypes.Add ("xgz", "xgl/drawing"); mimeTypes.Add ("xhtml", "application/xhtml+xml"); mimeTypes.Add ("xht", "application/xhtml+xml"); mimeTypes.Add ("xif", "image/vnd.xiff"); mimeTypes.Add ("xla", "application/excel"); mimeTypes.Add ("xl", "application/excel"); mimeTypes.Add ("xlb", "application/excel"); mimeTypes.Add ("xlc", "application/excel"); mimeTypes.Add ("xld", "application/excel"); mimeTypes.Add ("xlk", "application/excel"); mimeTypes.Add ("xll", "application/excel"); mimeTypes.Add ("xlm", "application/excel"); mimeTypes.Add ("xls", "application/excel"); mimeTypes.Add ("xlt", "application/excel"); mimeTypes.Add ("xlv", "application/excel"); mimeTypes.Add ("xlw", "application/excel"); mimeTypes.Add ("xm", "audio/xm"); mimeTypes.Add ("xml", "application/xml"); mimeTypes.Add ("xmz", "xgl/movie"); mimeTypes.Add ("xof", "x-world/x-vrml"); mimeTypes.Add ("xpi", "application/x-xpinstall"); mimeTypes.Add ("xpix", "application/x-vnd.ls-xpix"); mimeTypes.Add ("xpm", "image/xpm"); mimeTypes.Add ("x-png", "image/png"); mimeTypes.Add ("xsd", "application/xml"); mimeTypes.Add ("xsl", "application/xml"); mimeTypes.Add ("xsr", "video/x-amt-showrun"); mimeTypes.Add ("xwd", "image/x-xwd"); mimeTypes.Add ("xyz", "chemical/x-pdb"); mimeTypes.Add ("z", "application/x-compressed"); mimeTypes.Add ("zip", "application/zip"); mimeTypes.Add ("zsh", "text/x-script.zsh"); // Office Formats mimeTypes.Add("docm", "application/vnd.ms-word.document.macroEnabled.12"); mimeTypes.Add("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); mimeTypes.Add("dotm", "application/vnd.ms-word.template.macroEnabled.12"); mimeTypes.Add("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"); mimeTypes.Add("potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"); mimeTypes.Add("potx", "application/vnd.openxmlformats-officedocument.presentationml.template"); mimeTypes.Add("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"); mimeTypes.Add("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"); mimeTypes.Add("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"); mimeTypes.Add("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"); mimeTypes.Add("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); mimeTypes.Add("xlam", "application/vnd.ms-excel.addin.macroEnabled.12"); mimeTypes.Add("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"); mimeTypes.Add("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"); mimeTypes.Add("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); mimeTypes.Add("xltm", "application/vnd.ms-excel.template.macroEnabled.12"); mimeTypes.Add("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"); } /// /// Adds a new MIME type. /// /// File extension /// MIME type public static void AddType(string extension, string type) { mimeTypes.Add(extension, type); } /// /// Gets the MIME type for a file name. /// /// Name of the file. /// public static string GetMimeType (string fileName) { string result = null; int dot = fileName.LastIndexOf ('.'); if (dot != -1 && fileName.Length > dot + 1) mimeTypes.TryGetValue (fileName.Substring (dot + 1), out result); if (result == null) result = "application/octet-stream"; return result; } } } ================================================ FILE: src/Nancy/ModelBinding/BindingConfig.cs ================================================ namespace Nancy.ModelBinding { /// /// Configurations that controls the behavior of the binder at runtime. /// public class BindingConfig { /// /// Initializes a new instance of the class. /// public BindingConfig() { this.Overwrite = true; } /// /// Binding configuration that permits that the binder overwrites non-default values. /// public static readonly BindingConfig NoOverwrite = new BindingConfig { Overwrite = false }; /// /// Default binding configuration. /// public static readonly BindingConfig Default = new BindingConfig(); /// /// Gets or sets whether the binder should be happy once it has bound to the request body. In this case, /// request and context parameters will not be bound to. If there is no body and this option is enabled, /// no binding will take place at all. /// /// if the binder will stop once the body has been bound, otherwise . public bool BodyOnly { get; set; } /// /// Gets or sets whether binding error should be ignored and the binder should continue with the next property. /// /// Setting this property to means that no will be thrown if an error occurs. /// If the binder should ignore errors, otherwise . public bool IgnoreErrors { get; set; } /// /// Gets or sets whether the binder is allowed to overwrite properties that does not have a default value. /// /// if the binder is allowed to overwrite non-default values, otherwise . public bool Overwrite { get; set; } } } ================================================ FILE: src/Nancy/ModelBinding/BindingContext.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections.Generic; /// /// Model binding context object /// public class BindingContext { /// /// The binding configuration /// public BindingConfig Configuration { get; set; } /// /// Current Nancy context /// public NancyContext Context { get; set; } /// /// Binding destination type /// public Type DestinationType { get; set; } /// /// The generic type of a collection is only used when DestinationType is a enumerable. /// public Type GenericType { get; set; } /// /// The current model object (or null for body deserialization) /// public object Model { get; set; } /// /// DestinationType properties that are not black listed /// public IEnumerable ValidModelBindingMembers { get; set; } /// /// The incoming data fields /// public IDictionary RequestData { get; set; } /// /// Available type converters - user converters followed by any defaults /// public IEnumerable TypeConverters { get; set; } } } ================================================ FILE: src/Nancy/ModelBinding/BindingDefaults.cs ================================================ namespace Nancy.ModelBinding { using System.Collections.Generic; using Nancy.Configuration; using Nancy.ModelBinding.DefaultBodyDeserializers; using Nancy.ModelBinding.DefaultConverters; /// /// Provides default binding converters/deserializers /// The defaults have less precedence than any user supplied ones /// public class BindingDefaults { private readonly IEnumerable defaultTypeConverters; private readonly IEnumerable defaultBodyDeserializers; /// /// Initializes a new instance of the class, /// with the provided . /// /// An instance. public BindingDefaults(INancyEnvironment environment) { // Ordering is important - for now we will new just these up // as the binding defaults class itself is replaceable if necessary, // and none of defaults have any dependencies. this.defaultTypeConverters = new ITypeConverter[] { new CollectionConverter(), new FallbackConverter(), }; this.defaultBodyDeserializers = new IBodyDeserializer[] { new JsonBodyDeserializer(environment), new XmlBodyDeserializer() }; } /// /// Gets the default type converters /// /// An of instances. public virtual IEnumerable DefaultTypeConverters { get { return this.defaultTypeConverters; } } /// /// Gets the default type converters /// /// An of instances. public virtual IEnumerable DefaultBodyDeserializers { get { return this.defaultBodyDeserializers; } } } } ================================================ FILE: src/Nancy/ModelBinding/BindingMemberInfo.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; /// /// Represents a bindable member of a type, which can be a property or a field. /// public class BindingMemberInfo { PropertyInfo propertyInfo; FieldInfo fieldInfo; /// /// Gets a reference to the MemberInfo that this BindingMemberInfo represents. This can be a property or a field. /// public MemberInfo MemberInfo { get { return this.propertyInfo ?? (MemberInfo)this.fieldInfo; } } /// /// Gets the name of the property or field represented by this BindingMemberInfo. /// public string Name { get { return this.MemberInfo.Name; } } /// /// Gets the data type of the property or field represented by this BindingMemberInfo. /// public Type PropertyType { get { if (this.propertyInfo != null) { return this.propertyInfo.PropertyType; } else { return this.fieldInfo.FieldType; } } } /// /// Constructs a BindingMemberInfo instance for a property. /// /// The bindable property to represent. public BindingMemberInfo(PropertyInfo propertyInfo) { if (propertyInfo == null) { throw new ArgumentNullException("propertyInfo"); } this.propertyInfo = propertyInfo; } /// /// Constructs a BindingMemberInfo instance for a field. /// /// The bindable field to represent. public BindingMemberInfo(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException("fieldInfo"); } this.fieldInfo = fieldInfo; } /// /// Gets the value from a specified object associated with the property or field represented by this BindingMemberInfo. /// /// The object whose property or field should be retrieved. /// The value for this BindingMemberInfo's property or field in the specified object. public object GetValue(object sourceObject) { if (this.propertyInfo != null) { return this.propertyInfo.GetValue(sourceObject, null); } else { return this.fieldInfo.GetValue(sourceObject); } } /// /// Sets the value from a specified object associated with the property or field represented by this BindingMemberInfo. /// /// The object whose property or field should be assigned. /// The value to assign in the specified object to this BindingMemberInfo's property or field. public void SetValue(object destinationObject, object newValue) { if (this.propertyInfo != null) { this.propertyInfo.SetValue(destinationObject, newValue, null); } else { this.fieldInfo.SetValue(destinationObject, newValue); } } /// public override bool Equals(object obj) { if (obj == null) { return false; } var other = obj as BindingMemberInfo; if (other == null) { return false; } return this.MemberInfo.Equals(other.MemberInfo); } /// /// Compares two BindingMemberInfo's with eachother on their respective values rather then their reference /// /// the other BindingMemberInfo /// true when they are equal and false otherwise public bool Equals(BindingMemberInfo obj) { if (obj == null) { return false; } return this.MemberInfo.Equals(obj.MemberInfo); } /// public override int GetHashCode() { return this.MemberInfo.GetHashCode(); } /// /// Returns an enumerable sequence of bindable properties for the specified type. /// /// The type to enumerate. /// Bindable properties. public static IEnumerable Collect() { return Collect(typeof(T)); } /// /// Returns an enumerable sequence of bindable properties for the specified type. /// /// The type to enumerate. /// Bindable properties. public static IEnumerable Collect(Type type) { var fromProperties = type .GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite) .Where(property => !property.GetIndexParameters().Any()) .Select(property => new BindingMemberInfo(property)); var fromFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => !f.IsInitOnly) .Select(field => new BindingMemberInfo(field)); return fromProperties.Concat(fromFields); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultBinder.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Nancy.Extensions; using Nancy.Responses.Negotiation; /// /// Default binder - used as a fallback when a specific modelbinder /// is not available. /// public class DefaultBinder : IBinder { private readonly IEnumerable typeConverters; private readonly IEnumerable bodyDeserializers; private readonly IFieldNameConverter fieldNameConverter; private readonly BindingDefaults defaults; private static readonly MethodInfo ToListMethodInfo = typeof(Enumerable).GetMethod("ToList", BindingFlags.Public | BindingFlags.Static); private static readonly MethodInfo ToArrayMethodInfo = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Public | BindingFlags.Static); private static readonly Regex BracketRegex = new Regex(@"\[(\d+)\]\z", RegexOptions.Compiled); private static readonly Regex UnderscoreRegex = new Regex(@"_(\d+)\z", RegexOptions.Compiled); /// /// Initializes a new instance of the class, with /// the provided , , /// and . /// /// The type converters. /// The body deserializers. /// The field name converter. /// The defaults for bindings. /// /// typeConverters /// or /// bodyDeserializers /// or /// fieldNameConverter /// or /// defaults /// public DefaultBinder(IEnumerable typeConverters, IEnumerable bodyDeserializers, IFieldNameConverter fieldNameConverter, BindingDefaults defaults) { if (typeConverters == null) { throw new ArgumentNullException("typeConverters"); } if (bodyDeserializers == null) { throw new ArgumentNullException("bodyDeserializers"); } if (fieldNameConverter == null) { throw new ArgumentNullException("fieldNameConverter"); } if (defaults == null) { throw new ArgumentNullException("defaults"); } this.typeConverters = typeConverters; this.bodyDeserializers = bodyDeserializers; this.fieldNameConverter = fieldNameConverter; this.defaults = defaults; } /// /// Bind to the given model type /// /// Current context /// Model type to bind to /// Optional existing instance /// The that should be applied during binding. /// Blacklisted binding property names /// Bound model public object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList) { Type genericType = null; if (modelType.IsArray() || modelType.IsCollection() || modelType.IsEnumerable()) { //make sure it has a generic type if (modelType.GetTypeInfo().IsGenericType) { genericType = modelType.GetGenericArguments().FirstOrDefault(); } else { var ienumerable = modelType.GetInterfaces().Where(i => i.GetTypeInfo().IsGenericType).FirstOrDefault( i => i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); genericType = ienumerable == null ? null : ienumerable.GetGenericArguments().FirstOrDefault(); } if (genericType == null) { throw new ArgumentException("When modelType is an enumerable it must specify the type.", "modelType"); } } var bindingContext = this.CreateBindingContext(context, modelType, instance, configuration, blackList, genericType); try { var bodyDeserializedModel = this.DeserializeRequestBody(bindingContext); if (bodyDeserializedModel != null) { UpdateModelWithDeserializedModel(bodyDeserializedModel, bindingContext); } } catch (Exception exception) { if (!bindingContext.Configuration.IgnoreErrors) { throw new ModelBindingException(modelType, innerException: exception); } } var bindingExceptions = new List(); if (!bindingContext.Configuration.BodyOnly) { if (bindingContext.DestinationType.IsCollection() || bindingContext.DestinationType.IsArray() ||bindingContext.DestinationType.IsEnumerable()) { var loopCount = this.GetBindingListInstanceCount(context); var model = (IList)bindingContext.Model; for (var i = 0; i < loopCount; i++) { object genericinstance; if (model.Count > i) { genericinstance = model[i]; } else { genericinstance = bindingContext.GenericType.CreateInstance(); model.Add(genericinstance); } foreach (var modelProperty in bindingContext.ValidModelBindingMembers) { var existingCollectionValue = modelProperty.GetValue(genericinstance); var collectionStringValue = GetValue(modelProperty.Name, bindingContext, i); if (this.BindingValueIsValid(collectionStringValue, existingCollectionValue, modelProperty, bindingContext)) { try { BindValue(modelProperty, collectionStringValue, bindingContext, genericinstance); } catch (PropertyBindingException ex) { bindingExceptions.Add(ex); } } } } } else { foreach (var modelProperty in bindingContext.ValidModelBindingMembers) { var existingValue = modelProperty.GetValue(bindingContext.Model); var stringValue = GetValue(modelProperty.Name, bindingContext); if (this.BindingValueIsValid(stringValue, existingValue, modelProperty, bindingContext)) { try { BindValue(modelProperty, stringValue, bindingContext); } catch (PropertyBindingException ex) { bindingExceptions.Add(ex); } } } } if (bindingExceptions.Any() && !bindingContext.Configuration.IgnoreErrors) { throw new ModelBindingException(modelType, bindingExceptions); } } if (modelType.IsArray()) { var generictoArrayMethod = ToArrayMethodInfo.MakeGenericMethod(new[] { genericType }); return generictoArrayMethod.Invoke(null, new[] { bindingContext.Model }); } return bindingContext.Model; } private bool BindingValueIsValid(string bindingValue, object existingValue, BindingMemberInfo modelProperty, BindingContext bindingContext) { return (!string.IsNullOrEmpty(bindingValue) && (IsDefaultValue(existingValue, modelProperty.PropertyType) || bindingContext.Configuration.Overwrite)); } /// /// Gets the number of distinct indexes from context: /// /// i.e: /// IntProperty_5 /// StringProperty_5 /// IntProperty_7 /// StringProperty_8 /// You'll end up with a list of 3 matches: 5,7,8 /// /// /// Current Context /// An int containing the number of elements private int GetBindingListInstanceCount(NancyContext context) { var dictionary = context.Request.Form as IDictionary; if (dictionary == null) { return 0; } return dictionary.Keys.Select(IsMatch).Where(x => x != -1).Distinct().ToArray().Length; } private static int IsMatch(string item) { var bracketMatch = BracketRegex.Match(item); if (bracketMatch.Success) { return int.Parse(bracketMatch.Groups[1].Value); } var underscoreMatch = UnderscoreRegex.Match(item); if (underscoreMatch.Success) { return int.Parse(underscoreMatch.Groups[1].Value); } return -1; } private static void UpdateModelWithDeserializedModel(object bodyDeserializedModel, BindingContext bindingContext) { var bodyDeserializedModelType = bodyDeserializedModel.GetType(); if (bodyDeserializedModelType.GetTypeInfo().IsValueType) { bindingContext.Model = bodyDeserializedModel; return; } if (bodyDeserializedModelType.IsCollection() || bodyDeserializedModelType.IsEnumerable() || bodyDeserializedModelType.IsArray()) { var count = 0; foreach (var o in (IEnumerable)bodyDeserializedModel) { var model = (IList)bindingContext.Model; if (o.GetType().GetTypeInfo().IsValueType || o is string) { HandleValueTypeCollectionElement(model, count, o); } else { HandleReferenceTypeCollectionElement(bindingContext, model, count, o); } count++; } } else { foreach (var modelProperty in bindingContext.ValidModelBindingMembers) { var existingValue = modelProperty.GetValue(bindingContext.Model); if (IsDefaultValue(existingValue, modelProperty.PropertyType) || bindingContext.Configuration.Overwrite) { CopyValue(modelProperty, bodyDeserializedModel, bindingContext.Model); } } } } private static void HandleValueTypeCollectionElement(IList model, int count, object o) { // If the instance specified in the binder contains the n-th element use that if (model.Count > count) { return; } model.Add(o); } private static void HandleReferenceTypeCollectionElement(BindingContext bindingContext, IList model, int count, object o) { // If the instance specified in the binder contains the n-th element use that otherwise make a new one. object genericTypeInstance; if (model.Count > count) { genericTypeInstance = model[count]; } else { genericTypeInstance = Activator.CreateInstance(bindingContext.GenericType); model.Add(genericTypeInstance); } foreach (var modelProperty in bindingContext.ValidModelBindingMembers) { var existingValue = modelProperty.GetValue(genericTypeInstance); if (IsDefaultValue(existingValue, modelProperty.PropertyType) || bindingContext.Configuration.Overwrite) { CopyValue(modelProperty, o, genericTypeInstance); } } } private static void CopyValue(BindingMemberInfo modelProperty, object source, object destination) { var newValue = modelProperty.GetValue(source); modelProperty.SetValue(destination, newValue); } private static bool IsDefaultValue(object existingValue, Type propertyType) { return propertyType.GetTypeInfo().IsValueType ? Equals(existingValue, Activator.CreateInstance(propertyType)) : existingValue == null; } private BindingContext CreateBindingContext(NancyContext context, Type modelType, object instance, BindingConfig configuration, IEnumerable blackList, Type genericType) { return new BindingContext { Configuration = configuration, Context = context, DestinationType = modelType, Model = CreateModel(modelType, genericType, instance), ValidModelBindingMembers = GetBindingMembers(modelType, genericType, blackList).ToList(), RequestData = this.GetDataFields(context), GenericType = genericType, TypeConverters = this.typeConverters.Concat(this.defaults.DefaultTypeConverters), }; } private IDictionary GetDataFields(NancyContext context) { var dictionaries = new IDictionary[] { ConvertDynamicDictionary(context.Request.Form), ConvertDynamicDictionary(context.Request.Query), ConvertDynamicDictionary(context.Parameters) }; return dictionaries.Merge(); } private IDictionary ConvertDynamicDictionary(DynamicDictionary dictionary) { if (dictionary == null) { return null; } return dictionary.GetDynamicMemberNames().ToDictionary( memberName => this.fieldNameConverter.Convert(memberName), memberName => (string)dictionary[memberName]); } private static void BindValue(BindingMemberInfo modelProperty, string stringValue, BindingContext context) { BindValue(modelProperty, stringValue, context, context.Model); } private static void BindValue(BindingMemberInfo modelProperty, string stringValue, BindingContext context, object targetInstance) { var destinationType = modelProperty.PropertyType; var typeConverter = context.TypeConverters.FirstOrDefault(c => c.CanConvertTo(destinationType, context)); if (typeConverter != null) { try { SetBindingMemberValue(modelProperty, targetInstance, typeConverter.Convert(stringValue, destinationType, context)); } catch (Exception e) { throw new PropertyBindingException(modelProperty.Name, stringValue, e); } } else if (destinationType == typeof(string)) { SetBindingMemberValue(modelProperty, targetInstance, stringValue); } } private static void SetBindingMemberValue(BindingMemberInfo modelProperty, object model, object value) { // TODO - catch reflection exceptions? modelProperty.SetValue(model, value); } private static IEnumerable GetBindingMembers(Type modelType, Type genericType, IEnumerable blackList) { var blackListHash = new HashSet(blackList, StringComparer.Ordinal); return BindingMemberInfo.Collect(genericType ?? modelType) .Where(member => !blackListHash.Contains(member.Name)); } private static object CreateModel(Type modelType, Type genericType, object instance) { if (modelType.IsArray() || modelType.IsCollection() || modelType.IsEnumerable()) { //make sure instance has a Add method. Otherwise call `.ToList` if (instance != null && modelType.IsInstanceOfType(instance)) { var addMethod = modelType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance); if (addMethod != null) { return instance; } var genericMethod = ToListMethodInfo.MakeGenericMethod(genericType); return genericMethod.Invoke(null, new[] { instance }); } //else just make a list var listType = typeof(List<>).MakeGenericType(genericType); return Activator.CreateInstance(listType); } if (instance == null) { return modelType.CreateInstance(true); } return !modelType.IsInstanceOfType(instance) ? modelType.CreateInstance(true) : instance; } private static string GetValue(string propertyName, BindingContext context, int index = -1) { if (index != -1) { var indexindexes = context.RequestData.Keys.Select(IsMatch) .Where(i => i != -1) .OrderBy(i => i) .Distinct() .Select((k, i) => new KeyValuePair(i, k)) .ToDictionary(k => k.Key, v => v.Value); int indexValue; if (indexindexes.TryGetValue(index, out indexValue)) { var propertyValue = context.RequestData.Where(c => { var indexId = IsMatch(c.Key); return c.Key.StartsWith(propertyName, StringComparison.OrdinalIgnoreCase) && indexId != -1 && indexId == indexValue; }) .Select(k => k.Value) .FirstOrDefault(); return propertyValue ?? string.Empty; } return string.Empty; } string value; return context.RequestData.TryGetValue(propertyName, out value) ? value : string.Empty; } private object DeserializeRequestBody(BindingContext context) { if (context.Context == null || context.Context.Request == null) { return null; } var contentType = GetRequestContentType(context.Context); if (contentType == null) { return null; } var bodyDeserializer = this.bodyDeserializers.FirstOrDefault(b => b.CanDeserialize(contentType, context)) ?? this.defaults.DefaultBodyDeserializers.FirstOrDefault(b => b.CanDeserialize(contentType, context)); return bodyDeserializer != null ? bodyDeserializer.Deserialize(contentType, context.Context.Request.Body, context) : null; } private static MediaRange GetRequestContentType(NancyContext context) { if (context == null || context.Request == null) { return null; } return context.Request.Headers.ContentType; } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultBodyDeserializers/JsonBodyDeserializer.cs ================================================ namespace Nancy.ModelBinding.DefaultBodyDeserializers { using System.IO; using System.Reflection; using Nancy.Configuration; using Nancy.Json; using Nancy.Responses.Negotiation; /// /// Deserializes request bodies in JSON format /// public class JsonBodyDeserializer : IBodyDeserializer { private readonly MethodInfo deserializeMethod = typeof(JavaScriptSerializer).GetMethod("Deserialize", BindingFlags.Instance | BindingFlags.Public); private readonly JsonConfiguration jsonConfiguration; private readonly GlobalizationConfiguration globalizationConfiguration; /// /// Initializes a new instance of the , /// with the provided . /// /// An instance. public JsonBodyDeserializer(INancyEnvironment environment) { this.jsonConfiguration = environment.GetValue(); this.globalizationConfiguration = environment.GetValue(); } /// /// Whether the deserializer can deserialize the content type /// /// Content type to deserialize /// Current . /// True if supported, false otherwise public bool CanDeserialize(MediaRange mediaRange, BindingContext context) { return Json.IsJsonContentType(mediaRange); } /// /// Deserialize the request body to a model /// /// Content type to deserialize /// Request body stream /// Current context /// Model instance public object Deserialize(MediaRange mediaRange, Stream bodyStream, BindingContext context) { var serializer = new JavaScriptSerializer(this.jsonConfiguration, this.globalizationConfiguration); serializer.RegisterConverters(this.jsonConfiguration.Converters, this.jsonConfiguration.PrimitiveConverters); if (bodyStream.CanSeek) { bodyStream.Position = 0; } string bodyText; using (var bodyReader = new StreamReader(bodyStream)) { bodyText = bodyReader.ReadToEnd(); } var genericDeserializeMethod = this.deserializeMethod.MakeGenericMethod(context.DestinationType); var deserializedObject = genericDeserializeMethod.Invoke(serializer, new object[] { bodyText }); return deserializedObject; } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultBodyDeserializers/XmlBodyDeserializer.cs ================================================ namespace Nancy.ModelBinding.DefaultBodyDeserializers { using System; using System.IO; using System.Xml.Serialization; using Nancy.Responses.Negotiation; /// /// Deserializes request bodies in XML format /// public class XmlBodyDeserializer : IBodyDeserializer { /// /// Whether the deserializer can deserialize the content type /// /// Content type to deserialize /// Current . /// True if supported, false otherwise public bool CanDeserialize(MediaRange mediaRange, BindingContext context) { if (string.IsNullOrEmpty(mediaRange)) { return false; } var contentMimeType = mediaRange.ToString().Split(';')[0]; return contentMimeType.Equals("application/xml", StringComparison.OrdinalIgnoreCase) || contentMimeType.Equals("text/xml", StringComparison.OrdinalIgnoreCase) || (contentMimeType.StartsWith("application/vnd", StringComparison.OrdinalIgnoreCase) && contentMimeType.EndsWith("+xml", StringComparison.OrdinalIgnoreCase)); } /// /// Deserialize the request body to a model /// /// Content type to deserialize /// Request body stream /// Current . /// Model instance public object Deserialize(MediaRange mediaRange, Stream bodyStream, BindingContext context) { if (bodyStream.CanSeek) { bodyStream.Position = 0; } var ser = new XmlSerializer(context.DestinationType); return ser.Deserialize(bodyStream); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultConverters/CollectionConverter.cs ================================================ namespace Nancy.ModelBinding.DefaultConverters { using System; using System.Linq; using System.Reflection; using Nancy.Extensions; /// /// Converter for handling enumerable types /// public class CollectionConverter : ITypeConverter { private readonly MethodInfo enumerableCastMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Public | BindingFlags.Static); private readonly MethodInfo enumerableToArrayMethod = typeof(Enumerable).GetMethod("ToArray", BindingFlags.Public | BindingFlags.Static); private readonly MethodInfo enumerableToListMethod = typeof(Enumerable).GetMethod("ToList", BindingFlags.Public | BindingFlags.Static); /// /// Whether the converter can convert to the destination type /// /// Destination type /// The current binding context /// True if conversion supported, false otherwise public bool CanConvertTo(Type destinationType, BindingContext context) { return destinationType.IsCollection() || destinationType.IsEnumerable() || destinationType.IsArray(); } /// /// Convert the string representation to the destination type /// /// Input string /// Destination type /// Current context /// Converted object of the destination type public object Convert(string input, Type destinationType, BindingContext context) { if (string.IsNullOrEmpty(input)) { return null; } var items = input.Split(','); // Strategy, schmategy ;-) if (destinationType.IsCollection()) { return this.ConvertCollection(items, destinationType, context); } if (destinationType.IsArray()) { return this.ConvertArray(items, destinationType, context); } if (destinationType.IsEnumerable()) { return this.ConvertEnumerable(items, destinationType, context); } return null; } private object ConvertCollection(string[] items, Type destinationType, BindingContext context) { var genericType = destinationType.GetGenericArguments().First(); var returnCollection = Activator.CreateInstance(destinationType); var converter = context.TypeConverters.FirstOrDefault(c => c.CanConvertTo(genericType, context)); if (converter == null) { return null; } var collectionAddMethod = destinationType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance); foreach (var item in items) { collectionAddMethod.Invoke(returnCollection, new[] { converter.Convert(item, genericType, context) }); } return returnCollection; } private object ConvertArray(string[] items, Type destinationType, BindingContext context) { var elementType = destinationType.GetElementType(); if (elementType == null) { return null; } var converter = context.TypeConverters.FirstOrDefault(c => c.CanConvertTo(elementType, context)); if (converter == null) { return null; } var returnArray = items.Select(s => converter.Convert(s, elementType, context)); var genericCastMethod = this.enumerableCastMethod.MakeGenericMethod(new[] { elementType }); var generictoArrayMethod = this.enumerableToArrayMethod.MakeGenericMethod(new[] { elementType }); var castArray = genericCastMethod.Invoke(null, new object[] { returnArray }); return generictoArrayMethod.Invoke(null, new[] { castArray }); } private object ConvertEnumerable(string[] items, Type destinationType, BindingContext context) { var genericType = destinationType.GetGenericArguments().First(); var converter = context.TypeConverters.FirstOrDefault(c => c.CanConvertTo(genericType, context)); if (converter == null) { return null; } var returnArray = items.Select(s => converter.Convert(s, genericType, context)); // Use ToList rather than AsEnumerable to make sure the collection // is materialised and converters are called as appropriate. var genericCastMethod = this.enumerableCastMethod.MakeGenericMethod(new[] { genericType }); var genericToListMethod = this.enumerableToListMethod.MakeGenericMethod(new[] { genericType }); var castArray = genericCastMethod.Invoke(null, new object[] { returnArray }); return genericToListMethod.Invoke(null, new[] { castArray }); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultConverters/DateTimeConverter.cs ================================================ namespace Nancy.ModelBinding.DefaultConverters { using System; /// /// Converter for datetime types /// public class DateTimeConverter : ITypeConverter { /// /// Whether the converter can convert to the destination type /// /// Destination type /// The current binding context /// True if conversion supported, false otherwise public bool CanConvertTo(Type destinationType, BindingContext context) { return destinationType == typeof(DateTime); } /// /// Convert the string representation to the destination type /// /// Input string /// Destination type /// Current context /// Converted object of the destination type public object Convert(string input, Type destinationType, BindingContext context) { if (string.IsNullOrEmpty(input)) { return null; } return System.Convert.ChangeType(input, destinationType, context.Context.Culture); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultConverters/FallbackConverter.cs ================================================ namespace Nancy.ModelBinding.DefaultConverters { using System; using System.ComponentModel; /// /// A fallback converter that uses TypeDescriptor.GetConverter to try /// and convert the value. /// public class FallbackConverter : ITypeConverter { /// /// Whether the converter can convert to the destination type /// /// Destination type /// The current binding context /// True if conversion supported, false otherwise public bool CanConvertTo(Type destinationType, BindingContext context) { return true; } /// /// Convert the string representation to the destination type /// /// Input string /// Destination type /// Current context /// Converted object of the destination type public object Convert(string input, Type destinationType, BindingContext context) { var converter = TypeDescriptor.GetConverter(destinationType); if (converter == null || !converter.CanConvertFrom(typeof(string))) { return null; } try { return converter.ConvertFrom(input); } catch (FormatException) { if (destinationType == typeof(bool) && converter.GetType() == typeof(BooleanConverter) && "on".Equals(input, StringComparison.OrdinalIgnoreCase)) { return true; } return null; } } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultConverters/NumericConverter.cs ================================================ namespace Nancy.ModelBinding.DefaultConverters { using System; using Nancy.Extensions; /// /// Converter for numeric types /// public class NumericConverter : ITypeConverter { /// /// Whether the converter can convert to the destination type /// /// Destination type /// The current binding context /// True if conversion supported, false otherwise public bool CanConvertTo(Type destinationType, BindingContext context) { return destinationType.IsNumeric(); } /// /// Convert the string representation to the destination type /// /// Input string /// Destination type /// Current context /// Converted object of the destination type public object Convert(string input, Type destinationType, BindingContext context) { if (string.IsNullOrEmpty(input)) { return null; } return System.Convert.ChangeType(input, destinationType, context.Context.Culture); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultFieldNameConverter.cs ================================================ namespace Nancy.ModelBinding { using System.Collections.Concurrent; /// /// Default field name converter /// Converts camel case to pascal case /// public class DefaultFieldNameConverter : IFieldNameConverter { private readonly ConcurrentDictionary cache; /// /// Initializes a new instance of the class. /// public DefaultFieldNameConverter() { this.cache = new ConcurrentDictionary(); } /// /// Converts a field name to a property name /// /// Field name /// Property name public string Convert(string fieldName) { if (string.IsNullOrWhiteSpace(fieldName)) { return fieldName; } return this.cache.GetOrAdd(fieldName, name => { if (name.Length > 1) { return char.ToUpperInvariant(name[0]) + name.Substring(1); } return name.ToUpperInvariant(); }); } } } ================================================ FILE: src/Nancy/ModelBinding/DefaultModelBinderLocator.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections.Generic; using System.Linq; /// /// Locates model binders for a particular model /// public class DefaultModelBinderLocator : IModelBinderLocator { /// /// Available model binders /// private readonly IReadOnlyCollection binders; /// /// Default model binder to fall back on /// private readonly IBinder fallbackBinder; /// /// Initializes a new instance of the class. /// /// Available model binders /// Fallback binder public DefaultModelBinderLocator(IEnumerable binders, IBinder fallbackBinder) { this.fallbackBinder = fallbackBinder; if (binders != null) { this.binders = binders.ToArray(); } } /// /// Gets a binder for the given type /// /// Destination type to bind to /// The instance of the current request. /// IModelBinder instance or null if none found public IBinder GetBinderForType(Type modelType, NancyContext context) { if (this.binders == null) { return this.fallbackBinder; } foreach (var modelBinder in this.binders) { if (modelBinder.CanBind(modelType)) { return modelBinder; } } return this.fallbackBinder; } } } ================================================ FILE: src/Nancy/ModelBinding/DynamicModelBinderAdapter.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Dynamic; /// /// Provides wiring up of a model binder when cast to a destination type /// public class DynamicModelBinderAdapter : DynamicObject { private readonly IModelBinderLocator locator; private readonly NancyContext context; private readonly object instance; private readonly BindingConfig configuration; private readonly string[] blacklistedProperties; /// /// Initializes a new instance of the class. /// /// Model binder locator /// Nancy context /// Optional existing instance, or null /// The that should be applied during binding. /// Blacklisted property names public DynamicModelBinderAdapter(IModelBinderLocator locator, NancyContext context, object instance, BindingConfig configuration, params string[] blacklistedProperties) { if (locator == null) { throw new ArgumentNullException("locator"); } if (context == null) { throw new ArgumentNullException("context"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } this.locator = locator; this.context = context; this.instance = instance; this.configuration = configuration; this.blacklistedProperties = blacklistedProperties; } /// /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. /// /// /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) /// /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.The result of the type conversion operation. public override bool TryConvert(ConvertBinder binder, out object result) { var instanceType = instance == null ? binder.Type : this.instance.GetType(); var modelBinder = this.locator.GetBinderForType(instanceType, this.context); if (modelBinder == null) { throw new ModelBindingException(instanceType); } result = modelBinder.Bind(this.context, instanceType, this.instance, this.configuration, this.blacklistedProperties); return result != null || base.TryConvert(binder, out result); } } } ================================================ FILE: src/Nancy/ModelBinding/ExpressionExtensions.cs ================================================ namespace Nancy.ModelBinding { using System.Linq.Expressions; using System.Reflection; /// /// Contains extension methods for the type. /// public static class ExpressionExtensions { /// /// Retrieves the member that an expression is defined for. /// /// The expression to retrieve the member from. /// A instance if the member could be found; otherwise . public static MemberInfo GetTargetMemberInfo(this Expression expression) { switch (expression.NodeType) { case ExpressionType.Convert: return GetTargetMemberInfo(((UnaryExpression)expression).Operand); case ExpressionType.Lambda: return GetTargetMemberInfo(((LambdaExpression)expression).Body); case ExpressionType.Call: return ((MethodCallExpression)expression).Method; case ExpressionType.MemberAccess: return ((MemberExpression)expression).Member; default: return null; } } } } ================================================ FILE: src/Nancy/ModelBinding/IBinder.cs ================================================ namespace Nancy.ModelBinding { using System; /// /// Binds incoming request data to a model type /// public interface IBinder { /// /// Bind to the given model type /// /// Current context /// Model type to bind to /// The that should be applied during binding. /// Blacklisted property names /// Existing instance of the object /// Bound model object Bind(NancyContext context, Type modelType, object instance, BindingConfig configuration, params string[] blackList); } } ================================================ FILE: src/Nancy/ModelBinding/IBodyDeserializer.cs ================================================ namespace Nancy.ModelBinding { using System.IO; using Nancy.Responses.Negotiation; /// /// Provides a way to deserialize the contents of a request /// into a bound model. /// public interface IBodyDeserializer { /// /// Whether the deserializer can deserialize the content type /// /// Content type to deserialize /// Current . /// True if supported, false otherwise bool CanDeserialize(MediaRange mediaRange, BindingContext context); /// /// Deserialize the request body to a model /// /// Content type to deserialize /// Request body stream /// Current . /// Model instance object Deserialize(MediaRange mediaRange, Stream bodyStream, BindingContext context); } } ================================================ FILE: src/Nancy/ModelBinding/IFieldNameConverter.cs ================================================ namespace Nancy.ModelBinding { /// /// Provides the capability to supply a convention to /// convert form field names to property names if required. /// public interface IFieldNameConverter { /// /// Converts a field name to a property name /// /// Field name /// Property name string Convert(string fieldName); } } ================================================ FILE: src/Nancy/ModelBinding/IModelBinder.cs ================================================ namespace Nancy.ModelBinding { using System; /// /// Provides a way to bind an incoming request, via the context, to a model type /// public interface IModelBinder : IBinder { /// /// Whether the binder can bind to the given model type /// /// Required model type /// True if binding is possible, false otherwise bool CanBind(Type modelType); } } ================================================ FILE: src/Nancy/ModelBinding/IModelBinderLocator.cs ================================================ namespace Nancy.ModelBinding { using System; /// /// Locates model binders for a particular model /// public interface IModelBinderLocator { /// /// Gets a binder for the given type /// /// Destination type to bind to /// The instance of the current request. /// IModelBinder instance or null if none found IBinder GetBinderForType(Type modelType, NancyContext context); } } ================================================ FILE: src/Nancy/ModelBinding/ITypeConverter.cs ================================================ namespace Nancy.ModelBinding { using System; /// /// Provides a way to convert from the incoming string representation /// of a type to the type itself. /// public interface ITypeConverter { /// /// Whether the converter can convert to the destination type /// /// Destination type /// The current binding context /// True if conversion supported, false otherwise bool CanConvertTo(Type destinationType, BindingContext context); /// /// Convert the string representation to the destination type /// /// Input string /// Destination type /// Current context /// Converted object of the destination type object Convert(string input, Type destinationType, BindingContext context); } } ================================================ FILE: src/Nancy/ModelBinding/ModelBindingException.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections.Generic; using System.Runtime.Serialization; /// /// Represents an exception when attempting to bind to a model /// public class ModelBindingException : Exception { private const string ExceptionMessage = "Unable to bind to type: {0}"; /// /// Gets all failures /// public virtual IEnumerable PropertyBindingExceptions { get; private set; } /// /// Gets the model type, which caused the exception /// public virtual Type BoundType { get; private set; } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// the model type to bind to /// the original exceptions, thrown while binding the property /// The inner exception. public ModelBindingException(Type boundType, IEnumerable propertyBindingExceptions = null, Exception innerException = null) : base(string.Format(ExceptionMessage, boundType), innerException) { if (boundType == null) { throw new ArgumentNullException("boundType"); } this.PropertyBindingExceptions = propertyBindingExceptions ?? new List(); this.BoundType = boundType; } #if !NETSTANDARD1_6 /// /// Initializes a new instance of the class with serialized data. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. protected ModelBindingException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } } ================================================ FILE: src/Nancy/ModelBinding/ModuleExtensions.cs ================================================ namespace Nancy.ModelBinding { using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Nancy.Validation; /// /// A convenience class that contains various extension methods for modules. /// public static class ModuleExtensions { private static readonly string[] NoBlacklistedProperties = ArrayCache.Empty(); /// /// Parses an array of expressions like t => t.Property to a list of strings containing the property names; /// /// Type of the model /// Expressions that tell which property should be ignored /// Array of strings containing the names of the properties. private static string[] ParseBlacklistedPropertiesExpressionTree(this IEnumerable>> expressions) { return expressions.Select(p => p.GetTargetMemberInfo().Name).ToArray(); } /// /// Bind the incoming request to a model /// /// Current module /// Property names to blacklist from binding /// Model adapter - cast to a model type to bind it public static dynamic Bind(this INancyModule module, params string[] blacklistedProperties) { return module.Bind(BindingConfig.Default, blacklistedProperties); } /// /// Bind the incoming request to a model /// /// Current module /// The that should be applied during binding. /// Property names to blacklist from binding /// Model adapter - cast to a model type to bind it public static dynamic Bind(this INancyModule module, BindingConfig configuration, params string[] blacklistedProperties) { return new DynamicModelBinderAdapter(module.ModelBinderLocator, module.Context, null, configuration, blacklistedProperties); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// Bound model instance public static TModel Bind(this INancyModule module) { return module.Bind(); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// Property names to blacklist from binding /// Bound model instance public static TModel Bind(this INancyModule module, params string[] blacklistedProperties) { return module.Bind(blacklistedProperties); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// Bound model instance public static TModel Bind(this INancyModule module, params Expression>[] blacklistedProperties) { return module.Bind(blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// Property names to blacklist from binding /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module, params string[] blacklistedProperties) { var model = module.Bind(blacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module, params Expression>[] blacklistedProperties) { var model = module.Bind(blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); module.Validate(model); return model; } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module) { var model = module.Bind(NoBlacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// The that should be applied during binding. /// Bound model instance public static TModel Bind(this INancyModule module, BindingConfig configuration) { return module.Bind(configuration); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// The that should be applied during binding. /// Property names to blacklist from binding /// Bound model instance public static TModel Bind(this INancyModule module, BindingConfig configuration, params string[] blacklistedProperties) { return module.Bind(configuration, blacklistedProperties); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// The that should be applied during binding. /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// Bound model instance public static TModel Bind(this INancyModule module, BindingConfig configuration, Expression> blacklistedProperty) { return module.Bind(configuration, new [] { blacklistedProperty }.ParseBlacklistedPropertiesExpressionTree()); } /// /// Bind the incoming request to a model /// /// Model type /// Current module /// The that should be applied during binding. /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// Bound model instance public static TModel Bind(this INancyModule module, BindingConfig configuration, params Expression>[] blacklistedProperties) { return module.Bind(configuration, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// The that should be applied during binding. /// Property names to blacklist from binding /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module, BindingConfig configuration, params string[] blacklistedProperties) { var model = module.Bind(configuration, blacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// The that should be applied during binding. /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module, BindingConfig configuration, params Expression>[] blacklistedProperties) { var model = module.Bind(configuration, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); module.Validate(model); return model; } /// /// Bind the incoming request to a model and validate /// /// Model type /// Current module /// The that should be applied during binding. /// Bound model instance /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindAndValidate(this INancyModule module, BindingConfig configuration) { var model = module.Bind(configuration, NoBlacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to /// Property names to blacklist from binding public static TModel BindTo(this INancyModule module, TModel instance, params string[] blacklistedProperties) { return module.BindTo(instance, BindingConfig.NoOverwrite, blacklistedProperties); } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) public static TModel BindTo(this INancyModule module, TModel instance, params Expression>[] blacklistedProperties) { return module.BindTo(instance, BindingConfig.NoOverwrite, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to public static TModel BindTo(this INancyModule module, TModel instance) { return module.BindTo(instance, BindingConfig.NoOverwrite, NoBlacklistedProperties); } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// Property names to blacklist from binding /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindToAndValidate(this INancyModule module, TModel instance, params string[] blacklistedProperties) { var model = module.BindTo(instance, blacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindToAndValidate(this INancyModule module, TModel instance, params Expression>[] blacklistedProperties) { var model = module.BindTo(instance, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindToAndValidate(this INancyModule module, TModel instance) { var model = module.BindTo(instance, NoBlacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. /// Property names to blacklist from binding public static TModel BindTo(this INancyModule module, TModel instance, BindingConfig configuration, params string[] blacklistedProperties) { dynamic adapter = new DynamicModelBinderAdapter(module.ModelBinderLocator, module.Context, instance, configuration, blacklistedProperties); return adapter; } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. /// Expressions that tell which property should be ignored /// this.Bind<Person>(p => p.Name, p => p.Age) public static TModel BindTo(this INancyModule module, TModel instance, BindingConfig configuration, params Expression>[] blacklistedProperties) { return module.BindTo(instance, configuration, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); } /// /// Bind the incoming request to an existing instance /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. public static TModel BindTo(this INancyModule module, TModel instance, BindingConfig configuration) { return module.BindTo(instance, configuration, NoBlacklistedProperties); } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. /// Property names to blacklist from binding /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindToAndValidate(this INancyModule module, TModel instance, BindingConfig configuration, params string[] blacklistedProperties) { var model = module.BindTo(instance, configuration, blacklistedProperties); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. /// Expressions that tell which property should be ignored /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. /// this.BindToAndValidate(person, config, p => p.Name, p => p.Age) public static TModel BindToAndValidate(this INancyModule module, TModel instance, BindingConfig configuration, params Expression>[] blacklistedProperties) { var model = module.BindTo(instance, configuration, blacklistedProperties.ParseBlacklistedPropertiesExpressionTree()); module.Validate(model); return model; } /// /// Bind the incoming request to an existing instance and validate /// /// Model type /// Current module /// The class instance to bind properties to /// The that should be applied during binding. /// is stored in NancyModule.ModelValidationResult and NancyContext.ModelValidationResult. public static TModel BindToAndValidate(this INancyModule module, TModel instance, BindingConfig configuration) { var model = module.BindTo(instance, configuration, NoBlacklistedProperties); module.Validate(model); return model; } } } ================================================ FILE: src/Nancy/ModelBinding/PropertyBindingException.cs ================================================ namespace Nancy.ModelBinding { using System; /// /// Represents an exception occurred when binding the properties. /// /// public class PropertyBindingException : Exception { private const string ExceptionMessage = "Unable to bind property: {0}; Attempted value: {1}"; /// /// Gets the property name for which the bind failed /// public string PropertyName { get; private set; } /// /// Gets the value which was attempted to be assigned to the property /// public string AttemptedValue { get; private set; } /// /// Creates new instance /// /// the name of the property which failed to bind /// the value attempted to set /// the underlying exception public PropertyBindingException(string propertyName, string attemptedValue, Exception innerException = null) : base(String.Format(ExceptionMessage, propertyName, attemptedValue), innerException) { this.PropertyName = propertyName; this.AttemptedValue = attemptedValue; } } } ================================================ FILE: src/Nancy/NamedPipelineBase.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; /// /// Abstract base class for named pipelines. /// /// The type of the delegate. public abstract class NamedPipelineBase { /// /// Pipeline items to execute /// protected readonly List> pipelineItems; /// /// Initializes a new instance of the class. /// protected NamedPipelineBase() { this.pipelineItems = new List>(); } /// /// Initializes a new instance of the class. /// /// The number of pipeline delegates. protected NamedPipelineBase(int capacity) { this.pipelineItems = new List>(capacity); } /// /// Gets the current pipeline items /// public IEnumerable> PipelineItems { get { return this.pipelineItems.AsReadOnly(); } } /// /// Gets the current pipeline item delegates /// public IEnumerable PipelineDelegates { get { return this.pipelineItems.Select(pipelineItem => pipelineItem.Delegate); } } /// /// Add an item to the start of the pipeline /// /// Item to add public virtual void AddItemToStartOfPipeline(TDelegate item) { this.AddItemToStartOfPipeline((PipelineItem)item); } /// /// Add an item to the start of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToStartOfPipeline(PipelineItem item, bool replaceInPlace = false) { this.InsertItemAtPipelineIndex(0, item, replaceInPlace); } /// /// Add an item to the end of the pipeline /// /// Item to add public virtual void AddItemToEndOfPipeline(TDelegate item) { this.AddItemToEndOfPipeline((PipelineItem)item); } /// /// Add an item to the end of the pipeline /// /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void AddItemToEndOfPipeline(PipelineItem item, bool replaceInPlace = false) { var existingIndex = this.RemoveByName(item.Name); if (replaceInPlace && existingIndex != -1) { this.InsertItemAtPipelineIndex(existingIndex, item); } else { this.pipelineItems.Add(item); } } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add public virtual void InsertItemAtPipelineIndex(int index, TDelegate item) { this.InsertItemAtPipelineIndex(index, (PipelineItem)item); } /// /// Add an item to a specific place in the pipeline. /// /// Index to add at /// Item to add /// /// Whether to replace an existing item with the same name in its current place, /// rather than at the position requested. Defaults to false. /// public virtual void InsertItemAtPipelineIndex(int index, PipelineItem item, bool replaceInPlace = false) { var existingIndex = this.RemoveByName(item.Name); var newIndex = (replaceInPlace && existingIndex != -1) ? existingIndex : index; this.pipelineItems.Insert(newIndex, item); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, TDelegate item) { this.InsertBefore(name, (PipelineItem)item); } /// /// Insert an item before a named item. /// If the named item does not exist the item is inserted at the start of the pipeline. /// /// Name of the item to insert before /// Item to insert public virtual void InsertBefore(string name, PipelineItem item) { var existingIndex = this.pipelineItems.FindIndex(i => String.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex == -1) { existingIndex = 0; } this.InsertItemAtPipelineIndex(existingIndex, item); } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, TDelegate item) { this.InsertAfter(name, (PipelineItem)item); } /// /// Insert an item after a named item. /// If the named item does not exist the item is inserted at the end of the pipeline. /// /// Name of the item to insert after /// Item to insert public virtual void InsertAfter(string name, PipelineItem item) { var existingIndex = this.pipelineItems.FindIndex(i => String.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex == -1) { existingIndex = this.pipelineItems.Count; } existingIndex++; if (existingIndex > this.pipelineItems.Count) { this.AddItemToEndOfPipeline(item); } else { this.InsertItemAtPipelineIndex(existingIndex, item); } } /// /// Remove a named pipeline item /// /// Name /// Index of item that was removed or -1 if nothing removed public virtual int RemoveByName(string name) { if (string.IsNullOrEmpty(name)) { return -1; } var existingIndex = this.pipelineItems.FindIndex(i => String.Equals(name, i.Name, StringComparison.Ordinal)); if (existingIndex != -1) { this.pipelineItems.RemoveAt(existingIndex); } return existingIndex; } } } ================================================ FILE: src/Nancy/Nancy.csproj ================================================  Nancy is a lightweight web framework for the .Net platform, inspired by Sinatra. Nancy aim at delivering a low ceremony approach to building light, fast web applications. netstandard2.0;net452 ================================================ FILE: src/Nancy/NancyContext.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Claims; using Nancy.Configuration; using Nancy.Diagnostics; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Validation; /// /// Nancy context. /// public sealed class NancyContext : IDisposable { private Request request; private ModelValidationResult modelValidationResult; /// /// Initializes a new instance of the class. /// public NancyContext() { this.Items = new Dictionary(); this.Trace = new DefaultRequestTrace(); this.ViewBag = new DynamicDictionary(); this.NegotiationContext = new NegotiationContext(); // TODO - potentially additional logic to lock to ip etc? this.ControlPanelEnabled = true; } /// /// Gets the dictionary for storage of per-request items. Disposable items will be disposed when the context is. /// public IDictionary Items { get; private set; } /// /// Gets or sets the resolved route /// public Route ResolvedRoute { get; set; } /// /// Gets or sets the parameters for the resolved route /// public dynamic Parameters { get; set; } /// /// Gets or sets the incoming request /// public Request Request { get { return this.request; } set { this.request = value; this.Trace.RequestData = value; } } /// /// Gets or sets the outgoing response /// public Response Response { get; set; } /// /// Gets or sets the current user /// public ClaimsPrincipal CurrentUser { get; set; } /// /// Diagnostic request tracing /// public IRequestTrace Trace { get; set; } /// /// Gets a value indicating whether control panel access is enabled for this request /// public bool ControlPanelEnabled { get; private set; } /// /// Non-model specific data for rendering in the response /// public dynamic ViewBag { get; private set; } /// /// Gets or sets the model validation result. /// public ModelValidationResult ModelValidationResult { get { return this.modelValidationResult ?? (this.modelValidationResult = new ModelValidationResult()); } set { this.modelValidationResult = value; } } /// /// Gets or sets the context's culture /// public CultureInfo Culture { get; set; } /// /// Context of content negotiation (if relevant) /// public NegotiationContext NegotiationContext { get; set; } /// /// Gets or sets the dynamic object used to locate text resources. /// public dynamic Text { get; set; } /// /// Gets or sets the . /// /// An instance. public INancyEnvironment Environment { get; set; } /// /// Disposes any disposable items in the dictionary. /// public void Dispose() { foreach (var disposableItem in this.Items.Values.OfType()) { disposableItem.Dispose(); } this.Items.Clear(); if (this.request != null) { ((IDisposable) this.request).Dispose(); } if (this.Response != null) { this.Response.Dispose(); } } } } ================================================ FILE: src/Nancy/NancyEngine.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Nancy.Bootstrapper; using Cookies; using Diagnostics; using ErrorHandling; using Routing; using Helpers; using Nancy.Configuration; using Responses.Negotiation; /// /// Default engine for handling Nancy s. /// public class NancyEngine : INancyEngine { /// /// Key for error type /// public const string ERROR_KEY = "ERROR_TRACE"; /// /// Key for error exception message /// public const string ERROR_EXCEPTION = "ERROR_EXCEPTION"; private readonly IRequestDispatcher dispatcher; private readonly INancyContextFactory contextFactory; private readonly IRequestTracing requestTracing; private readonly IReadOnlyCollection statusCodeHandlers; private readonly IStaticContentProvider staticContentProvider; private readonly IResponseNegotiator negotiator; private readonly CancellationTokenSource engineDisposedCts; private readonly TraceConfiguration traceConfiguration; /// /// Initializes a new instance of the class. /// /// An instance that will be used to resolve a route, from the modules, that matches the incoming . /// A factory for creating contexts /// Error handlers /// The request tracing instance. /// The provider to use for serving static content /// The response negotiator. /// An instance. public NancyEngine(IRequestDispatcher dispatcher, INancyContextFactory contextFactory, IEnumerable statusCodeHandlers, IRequestTracing requestTracing, IStaticContentProvider staticContentProvider, IResponseNegotiator negotiator, INancyEnvironment environment) { if (dispatcher == null) { throw new ArgumentNullException("dispatcher", "The resolver parameter cannot be null."); } if (contextFactory == null) { throw new ArgumentNullException("contextFactory"); } if (statusCodeHandlers == null) { throw new ArgumentNullException("statusCodeHandlers"); } if (requestTracing == null) { throw new ArgumentNullException("requestTracing"); } if (staticContentProvider == null) { throw new ArgumentNullException("staticContentProvider"); } if (negotiator == null) { throw new ArgumentNullException("negotiator"); } this.dispatcher = dispatcher; this.contextFactory = contextFactory; this.statusCodeHandlers = statusCodeHandlers.ToArray(); this.requestTracing = requestTracing; this.staticContentProvider = staticContentProvider; this.negotiator = negotiator; this.engineDisposedCts = new CancellationTokenSource(); this.traceConfiguration = environment.GetValue(); } /// /// Factory for creating an instance for a incoming request. /// /// An instance. public Func RequestPipelinesFactory { get; set; } /// /// Handles an incoming async. /// /// An instance, containing the information about the current request. /// Delegate to call before the request is processed /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// The task object representing the asynchronous operation. public async Task HandleRequest(Request request, Func preRequest, CancellationToken cancellationToken) { using (var cts = CancellationTokenSource.CreateLinkedTokenSource(this.engineDisposedCts.Token, cancellationToken)) { cts.Token.ThrowIfCancellationRequested(); if (request == null) { throw new ArgumentNullException("request", "The request parameter cannot be null."); } var context = this.contextFactory.Create(request); if (preRequest != null) { context = preRequest(context); } var staticContentResponse = this.staticContentProvider.GetContent(context); if (staticContentResponse != null) { context.Response = staticContentResponse; return context; } var pipelines = this.RequestPipelinesFactory.Invoke(context); var nancyContext = await this.InvokeRequestLifeCycle(context, cts.Token, pipelines) .ConfigureAwait(false); this.CheckStatusCodeHandler(nancyContext); this.SaveTraceInformation(nancyContext); return nancyContext; } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public virtual void Dispose() { this.engineDisposedCts.Cancel(); } private void SaveTraceInformation(NancyContext ctx) { if (!this.EnableTracing(ctx)) { return; } if (ctx.Request == null || ctx.Response == null) { return; } var sessionGuid = this.GetDiagnosticsSessionGuid(ctx); ctx.Trace.RequestData = ctx.Request; ctx.Trace.ResponseData = ctx.Response; this.requestTracing.AddRequestDiagnosticToSession(sessionGuid, ctx); this.UpdateTraceCookie(ctx, sessionGuid); } private bool EnableTracing(NancyContext ctx) { return this.traceConfiguration.Enabled && !ctx.Items.ContainsKey(DiagnosticsHook.ItemsKey); } private Guid GetDiagnosticsSessionGuid(NancyContext ctx) { string sessionId; if (!ctx.Request.Cookies.TryGetValue("__NCTRACE", out sessionId)) { return this.requestTracing.CreateSession(); } Guid sessionGuid; if (!Guid.TryParse(sessionId, out sessionGuid)) { return this.requestTracing.CreateSession(); } if (!this.requestTracing.IsValidSessionId(sessionGuid)) { return this.requestTracing.CreateSession(); } return sessionGuid; } private void UpdateTraceCookie(NancyContext ctx, Guid sessionGuid) { var cookie = new NancyCookie("__NCTRACE", sessionGuid.ToString(), true) { Expires = DateTime.Now.AddMinutes(30) }; ctx.Response = ctx.Response.WithCookie(cookie); } private void CheckStatusCodeHandler(NancyContext context) { if (context.Response == null) { return; } IStatusCodeHandler defaultHandler = null; IStatusCodeHandler customHandler = null; foreach (var statusCodeHandler in this.statusCodeHandlers) { if (!statusCodeHandler.HandlesStatusCode(context.Response.StatusCode, context)) { continue; } if (defaultHandler == null && (statusCodeHandler is DefaultStatusCodeHandler)) { defaultHandler = statusCodeHandler; continue; } if (customHandler == null && !(statusCodeHandler is DefaultStatusCodeHandler)) { customHandler = statusCodeHandler; continue; } if ((defaultHandler != null) && (customHandler != null)) { break; } } var handler = customHandler ?? defaultHandler; if (handler == null) { return; } try { handler.Handle(context.Response.StatusCode, context); } catch (Exception) { if (defaultHandler == null) { throw; } defaultHandler.Handle(context.Response.StatusCode, context); } } private async Task InvokeRequestLifeCycle(NancyContext context, CancellationToken cancellationToken, IPipelines pipelines) { try { var response = await InvokePreRequestHook(context, cancellationToken, pipelines.BeforeRequest).ConfigureAwait(false) ?? await this.dispatcher.Dispatch(context, cancellationToken).ConfigureAwait(false); context.Response = response; await this.InvokePostRequestHook(context, cancellationToken, pipelines.AfterRequest).ConfigureAwait(false); await response.PreExecute(context).ConfigureAwait(false); } catch (Exception ex) { this.InvokeOnErrorHook(context, pipelines.OnError, ex); } return context; } private static Task InvokePreRequestHook(NancyContext context, CancellationToken cancellationToken, BeforePipeline pipeline) { return pipeline == null ? Task.FromResult(null) : pipeline.Invoke(context, cancellationToken); } private Task InvokePostRequestHook(NancyContext context, CancellationToken cancellationToken, AfterPipeline pipeline) { return pipeline == null ? TaskHelpers.CompletedTask : pipeline.Invoke(context, cancellationToken); } private void InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex) { try { if (pipeline == null) { throw new RequestExecutionException(ex); } var onErrorResult = pipeline.Invoke(context, ex); if (onErrorResult == null) { throw new RequestExecutionException(ex); } context.Response = this.negotiator.NegotiateResponse(onErrorResult, context); } catch (Exception e) { context.Response = new Response { StatusCode = HttpStatusCode.InternalServerError }; context.Items[ERROR_KEY] = e.ToString(); context.Items[ERROR_EXCEPTION] = e; } } } } ================================================ FILE: src/Nancy/NancyEngineExtensions.cs ================================================ namespace Nancy { using System.Threading; using System.Threading.Tasks; /// /// Extensions for Nancy engine /// public static class NancyEngineExtensions { /// /// Handles an incoming . /// /// The instance. /// An instance, containing the information about the current request. /// A instance containing the request/response context. public static Task HandleRequest(this INancyEngine nancyEngine, Request request) { return nancyEngine.HandleRequest(request, context => context, CancellationToken.None); } } } ================================================ FILE: src/Nancy/NancyModule.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Session; using Nancy.Validation; using Nancy.ViewEngines; /// /// Basic class containing the functionality for defining routes and actions in Nancy. /// public abstract class NancyModule : INancyModule, IHideObjectMembers { private readonly List routes; /// /// Initializes a new instance of the class. /// protected NancyModule() : this(string.Empty) { } /// /// Initializes a new instance of the class. /// /// A containing the root relative path that all paths in the module will be a subset of. protected NancyModule(string modulePath) { this.After = new AfterPipeline(); this.Before = new BeforePipeline(); this.OnError = new ErrorPipeline(); this.ModulePath = modulePath; this.routes = new List(); } /// /// Non-model specific data for rendering in the response /// public dynamic ViewBag { get { return this.Context == null ? null : this.Context.ViewBag; } } /// /// Dynamic access to text resources. /// public dynamic Text { get { return this.Context.Text; } } /// /// Declares a route for DELETE requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func action, Func condition = null, string name = null) { this.Delete(path, action, condition, name); } /// /// Declares a route for DELETE requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func action, Func condition = null, string name = null) { this.Delete(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for DELETE requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func> action, Func condition = null, string name = null) { this.Delete(path, action, condition, name); } /// /// Declares a route for DELETE requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func> action, Func condition = null, string name = null) { this.Delete(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for DELETE requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func> action, Func condition = null, string name = null) { this.Delete(path, action, condition, name); } /// /// Declares a route for DELETE requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Delete(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("DELETE", path, action, condition, name); } /// /// Declares a route for GET requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func action, Func condition = null, string name = null) { this.Get(path, action, condition, name); } /// /// Declares a route for GET requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func action, Func condition = null, string name = null) { this.Get(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for GET requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func> action, Func condition = null, string name = null) { this.Get(path, action, condition, name); } /// /// Declares a route for GET requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func> action, Func condition = null, string name = null) { this.Get(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for GET requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func> action, Func condition = null, string name = null) { this.Get(path, action, condition, name); } /// /// Declares a route for GET requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Get(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("GET", path, action, condition, name); } /// /// Declares a route for HEAD requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func action, Func condition = null, string name = null) { this.Head(path, action, condition, name); } /// /// Declares a route for HEAD requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func action, Func condition = null, string name = null) { this.Head(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for HEAD requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func> action, Func condition = null, string name = null) { this.Head(path, action, condition, name); } /// /// Declares a route for HEAD requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func> action, Func condition = null, string name = null) { this.Head(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for HEAD requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func> action, Func condition = null, string name = null) { this.Head(path, action, condition, name); } /// /// Declares a route for HEAD requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Head(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("HEAD", path, action, condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func action, Func condition = null, string name = null) { this.Options(path, action, condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func action, Func condition = null, string name = null) { this.Options(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func> action, Func condition = null, string name = null) { this.Options(path, action, condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func> action, Func condition = null, string name = null) { this.Options(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func> action, Func condition = null, string name = null) { this.Options(path, action, condition, name); } /// /// Declares a route for OPTIONS requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Options(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("OPTIONS", path, action, condition, name); } /// /// Declares a route for PATCH requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func action, Func condition = null, string name = null) { this.Patch(path, action, condition, name); } /// /// Declares a route for PATCH requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func action, Func condition = null, string name = null) { this.Patch(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for PATCH requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func> action, Func condition = null, string name = null) { this.Patch(path, action, condition, name); } /// /// Declares a route for PATCH requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func> action, Func condition = null, string name = null) { this.Patch(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for PATCH requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func> action, Func condition = null, string name = null) { this.Patch(path, action, condition, name); } /// /// Declares a route for PATCH requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Patch(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("PATCH", path, action, condition, name); } /// /// Declares a route for POST requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func action, Func condition = null, string name = null) { this.Post(path, action, condition, name); } /// /// Declares a route for POST requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func action, Func condition = null, string name = null) { this.Post(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for POST requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func> action, Func condition = null, string name = null) { this.Post(path, action, condition, name); } /// /// Declares a route for POST requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func> action, Func condition = null, string name = null) { this.Post(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for POST requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func> action, Func condition = null, string name = null) { this.Post(path, action, condition, name); } /// /// Declares a route for POST requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Post(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("POST", path, action, condition, name); } /// /// Declares a route for PUT requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func action, Func condition = null, string name = null) { this.Put(path, action, condition, name); } /// /// Declares a route for PUT requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func action, Func condition = null, string name = null) { this.Put(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name); } /// /// Declares a route for PUT requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func> action, Func condition = null, string name = null) { this.Put(path, action, condition, name); } /// /// Declares a route for PUT requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func> action, Func condition = null, string name = null) { this.Put(path, (args, ct) => action((DynamicDictionary)args), condition, name); } /// /// Declares a route for PUT requests. /// /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func> action, Func condition = null, string name = null) { this.Put(path, action, condition, name); } /// /// Declares a route for PUT requests. /// /// The return type of the /// The path that the route will respond to /// Action that will be invoked when the route it hit /// Name of the route /// A condition to determine if the route can be hit public virtual void Put(string path, Func> action, Func condition = null, string name = null) { this.AddRoute("PUT", path, action, condition, name); } /// /// Get the root path of the routes in the current module. /// /// /// A containing the root path of the module or /// if no root path should be used.All routes will be relative to this root path. /// public string ModulePath { get; protected set; } /// /// Gets all declared routes by the module. /// /// A instance, containing all instances declared by the module. /// This is automatically set by Nancy at runtime. [EditorBrowsable(EditorBrowsableState.Never)] public virtual IEnumerable Routes { get { return this.routes.AsReadOnly(); } } /// /// Gets the current session. /// public ISession Session { get { return this.Request.Session; } } /// /// Renders a view from inside a route handler. /// /// A instance that is used to determine which view that should be rendered. public ViewRenderer View { get { return new ViewRenderer(this); } } /// /// Used to negotiate the content returned based on Accepts header. /// /// A instance that is used to negotiate the content returned. public Negotiator Negotiate { get { return new Negotiator(this.Context); } } /// /// Gets or sets the validator locator. /// /// This is automatically set by Nancy at runtime. [EditorBrowsable(EditorBrowsableState.Never)] public IModelValidatorLocator ValidatorLocator { get; set; } /// /// Gets or sets an instance that represents the current request. /// /// An instance. public virtual Request Request { get { return this.Context.Request; } set { this.Context.Request = value; } } /// /// The extension point for accessing the view engines in Nancy. /// An instance. /// This is automatically set by Nancy at runtime. [EditorBrowsable(EditorBrowsableState.Never)] public IViewFactory ViewFactory { get; set; } /// /// The post-request hook /// /// The post-request hook is called after the response is created by the route execution. /// It can be used to rewrite the response or add/remove items from the context. /// /// This is automatically set by Nancy at runtime. /// public AfterPipeline After { get; set; } /// /// /// The pre-request hook /// /// /// The PreRequest hook is called prior to executing a route. If any item in the /// pre-request pipeline returns a response then the route is not executed and the /// response is returned. /// /// This is automatically set by Nancy at runtime. /// public BeforePipeline Before { get; set; } /// /// /// The error hook /// /// /// The error hook is called if an exception is thrown at any time during executing /// the PreRequest hook, a route and the PostRequest hook. It can be used to set /// the response and/or finish any ongoing tasks (close database session, etc). /// /// This is automatically set by Nancy at runtime. /// public ErrorPipeline OnError { get; set; } /// /// Gets or sets the current Nancy context /// /// A instance. /// This is automatically set by Nancy at runtime. public NancyContext Context { get; set; } /// /// An extension point for adding support for formatting response contents. /// This property will always return because it acts as an extension point.Extension methods to this property should always return or one of the types that can implicitly be types into a . public IResponseFormatter Response { get; set; } /// /// Gets or sets the model binder locator /// /// This is automatically set by Nancy at runtime. [EditorBrowsable(EditorBrowsableState.Never)] public IModelBinderLocator ModelBinderLocator { get; set; } /// /// Gets or sets the model validation result /// /// This is automatically set by Nancy at runtime when you run validation. public virtual ModelValidationResult ModelValidationResult { get { return this.Context == null ? null : this.Context.ModelValidationResult; } set { if (this.Context != null) { this.Context.ModelValidationResult = value; } } } /// /// Declares a route for the module /// /// /// Name of the route /// The HTTP method that the route will response to /// The path that the route will respond to /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit protected void AddRoute(string method, string path, Func> action, Func condition, string name) { this.routes.Add(new Route(name ?? string.Empty, method, this.GetFullPath(path), condition, action)); } private string GetFullPath(string path) { var relativePath = (path ?? string.Empty).Trim('/'); var parentPath = (this.ModulePath ?? string.Empty).Trim('/'); if (string.IsNullOrEmpty(parentPath)) { return string.Concat("/", relativePath); } if (string.IsNullOrEmpty(relativePath)) { return string.Concat("/", parentPath); } return string.Concat("/", parentPath, "/", relativePath); } } } ================================================ FILE: src/Nancy/NegotiatorExtensions.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Cookies; using Nancy.Responses.Negotiation; /// /// Extensions for negotioator /// public static class NegotiatorExtensions { /// /// Add a cookie to the response. /// /// The instance. /// The instance that should be added. /// The modified instance. public static Negotiator WithCookie(this Negotiator negotiator, INancyCookie cookie) { negotiator.NegotiationContext.Cookies.Add(cookie); return negotiator; } /// /// Add a collection of cookies to the response. /// /// The instance. /// The instances that should be added. /// The modified instance. public static Negotiator WithCookies(this Negotiator negotiator, IEnumerable cookies) { foreach (var cookie in cookies) { negotiator.WithCookie(cookie); } return negotiator; } /// /// Add a header to the response /// /// Negotiator object /// Header name /// Header value /// Modified negotiator public static Negotiator WithHeader(this Negotiator negotiator, string header, string value) { return negotiator.WithHeaders(new { Header = header, Value = value }); } /// /// Add a content type to the response /// /// Negotiator object /// Content type value /// Modified negotiator public static Negotiator WithContentType(this Negotiator negotiator, string contentType) { return negotiator.WithHeaders(new { Header = "Content-Type", Value = contentType }); } /// /// Adds headers to the response using anonymous types /// /// Negotiator object /// /// Array of headers - each header should be an anonymous type with two string properties /// 'Header' and 'Value' to represent the header name and its value. /// /// Modified negotiator public static Negotiator WithHeaders(this Negotiator negotiator, params object[] headers) { return negotiator.WithHeaders(headers.Select(GetTuple).ToArray()); } /// /// Adds headers to the response using anonymous types /// /// Negotiator object /// /// Array of headers - each header should be a Tuple with two string elements /// for header name and header value /// /// Modified negotiator public static Negotiator WithHeaders(this Negotiator negotiator, params Tuple[] headers) { foreach (var keyValuePair in headers) { negotiator.NegotiationContext.Headers[keyValuePair.Item1] = keyValuePair.Item2; } return negotiator; } /// /// Allows the response to be negotiated with any processors available for any content type /// /// Negotiator object /// Modified negotiator public static Negotiator WithFullNegotiation(this Negotiator negotiator) { negotiator.NegotiationContext.PermissableMediaRanges.Clear(); negotiator.NegotiationContext.PermissableMediaRanges.Add("*/*"); return negotiator; } /// /// Allows the response to be negotiated with a specific media range /// This will remove the wildcard range if it is already specified /// /// Negotiator object /// Media range to add /// Modified negotiator public static Negotiator WithAllowedMediaRange(this Negotiator negotiator, MediaRange mediaRange) { var wildcards = negotiator.NegotiationContext.PermissableMediaRanges.Where( mr => mr.Type.IsWildcard && mr.Subtype.IsWildcard).ToArray(); foreach (var wildcard in wildcards) { negotiator.NegotiationContext.PermissableMediaRanges.Remove(wildcard); } negotiator.NegotiationContext.PermissableMediaRanges.Add(mediaRange); return negotiator; } /// /// Uses the specified model as the default model for negotiation /// /// Negotiator object /// Model object /// Modified negotiator public static Negotiator WithModel(this Negotiator negotiator, dynamic model) { negotiator.NegotiationContext.DefaultModel = model; return negotiator; } /// /// Uses the specified view for html output /// /// Negotiator object /// View name /// Modified negotiator public static Negotiator WithView(this Negotiator negotiator, string viewName) { negotiator.NegotiationContext.ViewName = viewName; return negotiator; } /// /// Sets the model to use for a particular media range. /// Will also add the MediaRange to the allowed list /// /// Negotiator object /// Range to match against /// Model object /// Updated negotiator object public static Negotiator WithMediaRangeModel(this Negotiator negotiator, MediaRange range, object model) { return negotiator.WithMediaRangeModel(range, () => model); } /// /// Sets the model to use for a particular media range. /// Will also add the MediaRange to the allowed list /// /// Negotiator object /// Range to match against /// Model factory for returning the model object /// Updated negotiator object public static Negotiator WithMediaRangeModel(this Negotiator negotiator, MediaRange range, Func modelFactory) { negotiator.NegotiationContext.PermissableMediaRanges.Add(range); negotiator.NegotiationContext.MediaRangeModelMappings.Add(range, modelFactory); return negotiator; } /// /// Sets the to use for a particular media range. /// Will also add the MediaRange to the allowed list /// /// Negotiator object /// Range to match against /// A object /// Updated negotiator object public static Negotiator WithMediaRangeResponse(this Negotiator negotiator, MediaRange range, Response response) { return negotiator.WithMediaRangeResponse(range, () => response); } /// /// Sets the to use for a particular media range. /// Will also add the MediaRange to the allowed list /// /// Negotiator object /// Range to match against /// Factory for returning the object /// Updated negotiator object public static Negotiator WithMediaRangeResponse(this Negotiator negotiator, MediaRange range, Func responseFactory) { return negotiator.WithMediaRangeModel(range, responseFactory); } /// /// Sets the status code that should be assigned to the final response. /// /// Negotiator object /// The status code that should be used. /// Updated negotiator object public static Negotiator WithStatusCode(this Negotiator negotiator, int statusCode) { negotiator.NegotiationContext.StatusCode = (HttpStatusCode)statusCode; return negotiator; } /// /// Sets the description of the status code that should be assigned to the final response. /// /// Negotiator object /// The status code description that should be used. /// Updated negotiator object public static Negotiator WithReasonPhrase(this Negotiator negotiator, string reasonPhrase) { negotiator.NegotiationContext.ReasonPhrase = reasonPhrase; return negotiator; } /// /// Sets the status code that should be assigned to the final response. /// /// Negotiator object /// The status code that should be used. /// Updated negotiator object public static Negotiator WithStatusCode(this Negotiator negotiator, HttpStatusCode statusCode) { negotiator.NegotiationContext.StatusCode = statusCode; return negotiator; } private static Tuple GetTuple(object header) { var properties = header.GetType() .GetProperties() .Where(prop => prop.CanRead && prop.PropertyType == typeof(string)) .ToArray(); var headerProperty = properties .Where(p => string.Equals(p.Name, "Header", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); var valueProperty = properties .Where(p => string.Equals(p.Name, "Value", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); if (headerProperty == null || valueProperty == null) { throw new ArgumentException("Unable to extract 'Header' or 'Value' properties from anonymous type."); } return Tuple.Create( (string)headerProperty.GetValue(header, null), (string)valueProperty.GetValue(header, null)); } } } ================================================ FILE: src/Nancy/NotFoundResponse.cs ================================================ namespace Nancy { /// /// Not Found response /// /// public class NotFoundResponse : Response { /// /// Initializes a new instance of the class. /// public NotFoundResponse() { this.ContentType = "text/html"; this.StatusCode = HttpStatusCode.NotFound; } } } ================================================ FILE: src/Nancy/Owin/DelegateExtensions.cs ================================================ namespace Nancy.Owin { using System; using AppFunc = System.Func, System.Threading.Tasks.Task>; using MidFunc = System.Func, System.Threading.Tasks.Task>, System.Func, System.Threading.Tasks.Task>>; /// /// OWIN extensions for the delegate-based approach. /// public static class DelegateExtensions { /// /// Adds Nancy to the OWIN pipeline. /// /// The application builder delegate. /// A configuration builder action. /// The application builder delegate. public static Action UseNancy(this Action builder, Action action) { var options = new NancyOptions(); action(options); return builder.UseNancy(options); } /// /// Adds Nancy to the OWIN pipeline. /// /// The application builder delegate. /// The Nancy options. /// The application builder delegate. public static Action UseNancy(this Action builder, NancyOptions options = null) { var nancyOptions = options ?? new NancyOptions(); builder(NancyMiddleware.UseNancy(nancyOptions).Invoke); return builder; } } } ================================================ FILE: src/Nancy/Owin/NancyContextExtensions.cs ================================================ namespace Nancy.Owin { using System.Collections.Generic; /// /// OWIN extensions for the NancyContext. /// public static class NancyContextExtensions { /// /// Gets the OWIN environment dictionary. /// /// The Nancy context. /// The OWIN environment dictionary. public static IDictionary GetOwinEnvironment(this NancyContext context) { object environment; if (context.Items.TryGetValue(NancyMiddleware.RequestEnvironmentKey, out environment)) { return environment as IDictionary; } return null; } } } ================================================ FILE: src/Nancy/Owin/NancyMiddleware.cs ================================================ namespace Nancy.Owin { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Nancy.Helpers; using Nancy.IO; using AppFunc = System.Func, System.Threading.Tasks.Task>; using MidFunc = System.Func, System.Threading.Tasks.Task>, System.Func, System.Threading.Tasks.Task>>; /// /// Nancy middleware for OWIN. /// public static class NancyMiddleware { /// /// The request environment key /// public const string RequestEnvironmentKey = "OWIN_REQUEST_ENVIRONMENT"; /// /// Use Nancy in an OWIN pipeline /// /// A delegate to configure the . /// An OWIN middleware delegate. public static MidFunc UseNancy(Action configuration) { var options = new NancyOptions(); configuration(options); return UseNancy(options); } /// /// Use Nancy in an OWIN pipeline /// /// An to configure the Nancy middleware /// An OWIN middleware delegate. public static MidFunc UseNancy(NancyOptions options = null) { options = options ?? new NancyOptions(); options.Bootstrapper.Initialise(); var engine = options.Bootstrapper.GetEngine(); return next => async environment => { var owinRequestMethod = Get(environment, "owin.RequestMethod"); var owinRequestScheme = Get(environment, "owin.RequestScheme"); var owinRequestHeaders = Get>(environment, "owin.RequestHeaders"); var owinRequestPathBase = Get(environment, "owin.RequestPathBase"); var owinRequestPath = Get(environment, "owin.RequestPath"); var owinRequestQueryString = Get(environment, "owin.RequestQueryString"); var owinRequestBody = Get(environment, "owin.RequestBody"); var owinRequestProtocol = Get(environment, "owin.RequestProtocol"); var owinCallCancelled = Get(environment, "owin.CallCancelled"); var owinRequestHost = GetHeader(owinRequestHeaders, "Host") ?? Dns.GetHostName(); var owinUser = GetUser(environment); X509Certificate2 certificate = null; if (options.EnableClientCertificates) { certificate = new X509Certificate2(Get(environment, "ssl.ClientCertificate").Export(X509ContentType.Cert)); } var serverClientIp = Get(environment, "server.RemoteIpAddress"); var url = CreateUrl(owinRequestHost, owinRequestScheme, owinRequestPathBase, owinRequestPath, owinRequestQueryString); var expectedLength = ExpectedLength(owinRequestHeaders); // If length is 0 just use empty memory stream; as there is no body var nancyRequestStream = (expectedLength == 0) ? (Stream)new MemoryStream() : new RequestStream(owinRequestBody, expectedLength ?? 0, StaticConfiguration.DisableRequestStreamSwitching ?? false); var nancyRequest = new Request( owinRequestMethod, url, nancyRequestStream, owinRequestHeaders.ToDictionary(kv => kv.Key, kv => (IEnumerable)kv.Value, StringComparer.OrdinalIgnoreCase), serverClientIp, certificate, owinRequestProtocol); var nancyContext = await engine.HandleRequest( nancyRequest, StoreEnvironment(environment, owinUser), owinCallCancelled).ConfigureAwait(false); await RequestComplete(nancyContext, environment, options.PerformPassThrough, next).ConfigureAwait(false); }; } /// /// Gets a delegate to handle converting a nancy response /// to the format required by OWIN and signals that the we are /// now complete. /// /// The Nancy Context. /// OWIN environment. /// The next stage in the OWIN pipeline. /// A predicate that will allow the caller to determine if the request passes through to the /// next stage in the owin pipeline. /// Delegate private static Task RequestComplete( NancyContext context, IDictionary environment, Func performPassThrough, AppFunc next) { var owinResponseHeaders = Get>(environment, "owin.ResponseHeaders"); var owinResponseBody = Get(environment, "owin.ResponseBody"); var nancyResponse = context.Response; if (!performPassThrough(context)) { environment["owin.ResponseStatusCode"] = (int)nancyResponse.StatusCode; if (nancyResponse.ReasonPhrase != null) { environment["owin.ResponseReasonPhrase"] = nancyResponse.ReasonPhrase; } foreach (var responseHeader in nancyResponse.Headers) { owinResponseHeaders[responseHeader.Key] = new[] { responseHeader.Value }; } if (!string.IsNullOrWhiteSpace(nancyResponse.ContentType)) { owinResponseHeaders["Content-Type"] = new[] { nancyResponse.ContentType }; } if (nancyResponse.Cookies != null && nancyResponse.Cookies.Count != 0) { const string setCookieHeaderKey = "Set-Cookie"; string[] cookieHeader; string[] setCookieHeader = owinResponseHeaders.TryGetValue(setCookieHeaderKey, out cookieHeader) ? cookieHeader : ArrayCache.Empty(); owinResponseHeaders[setCookieHeaderKey] = setCookieHeader .Concat(nancyResponse.Cookies.Select(cookie => cookie.ToString())) .ToArray(); } nancyResponse.Contents(owinResponseBody); } else { return next(environment); } context.Dispose(); return TaskHelpers.CompletedTask; } private static T Get(IDictionary env, string key) { object value; return env.TryGetValue(key, out value) && value is T ? (T)value : default(T); } private static string GetHeader(IDictionary headers, string key) { string[] value; return headers.TryGetValue(key, out value) && value != null ? string.Join(",", value.ToArray()) : null; } private static ClaimsPrincipal GetUser(IDictionary environment) { // OWIN 1.1 object user; if (environment.TryGetValue("owin.RequestUser", out user)) { return user as ClaimsPrincipal; } // check for Katana User if (environment.TryGetValue("server.User", out user)) { return user as ClaimsPrincipal; } return null; } private static long? ExpectedLength(IDictionary headers) { var header = GetHeader(headers, "Content-Length"); if (string.IsNullOrWhiteSpace(header)) { header = GetHeader(headers, "Transfer-Encoding"); if (string.IsNullOrWhiteSpace(header)) { // No content-length or transfer-encoding means the length is definately 0 return 0; } // Has transfer-encoding, length is unknown return null; } // If length cannot be converted to an int, treat it as unknown return int.TryParse(header, NumberStyles.Any, CultureInfo.InvariantCulture, out int contentLength) ? contentLength : (long?)null; } /// /// Creates the Nancy URL /// /// OWIN Hostname /// OWIN Scheme /// OWIN Base path /// OWIN Path /// OWIN Querystring /// private static Url CreateUrl( string owinRequestHost, string owinRequestScheme, string owinRequestPathBase, string owinRequestPath, string owinRequestQueryString) { int? port = null; var hostnameParts = owinRequestHost.Split(':'); if (hostnameParts.Length == 2) { owinRequestHost = hostnameParts[0]; int tempPort; if (int.TryParse(hostnameParts[1], out tempPort)) { port = tempPort; } } var url = new Url { Scheme = owinRequestScheme, HostName = owinRequestHost, Port = port, BasePath = owinRequestPathBase, Path = owinRequestPath, Query = owinRequestQueryString, }; return url; } /// /// Gets a delegate to store the OWIN environment and flow the user into the NancyContext /// /// The OWIN environment. /// The user as a ClaimsPrincipal. /// Delegate private static Func StoreEnvironment(IDictionary environment, ClaimsPrincipal user) { return context => { context.CurrentUser = user; environment["nancy.NancyContext"] = context; context.Items[RequestEnvironmentKey] = environment; return context; }; } } } ================================================ FILE: src/Nancy/Owin/NancyOptions.cs ================================================ namespace Nancy.Owin { using System; using Nancy.Bootstrapper; /// /// Options for hosting Nancy with OWIN. /// public class NancyOptions { private INancyBootstrapper bootstrapper; private Func performPassThrough; /// /// Gets or sets the bootstrapper. If none is set, NancyBootstrapperLocator.Bootstrapper is used. /// public INancyBootstrapper Bootstrapper { get { return this.bootstrapper ?? NancyBootstrapperLocator.Bootstrapper; } set { this.bootstrapper = value; } } /// /// Gets or sets the delegate that determines if NancyMiddleware performs pass through. /// public Func PerformPassThrough { get { return this.performPassThrough ?? (context => false); } set { this.performPassThrough = value; } } /// /// Gets or sets a value indicating whether to request a client certificate or not. /// Defaults to false. /// public bool EnableClientCertificates { get; set; } } } ================================================ FILE: src/Nancy/Owin/NancyOptionsExtensions.cs ================================================ namespace Nancy.Owin { using System.Linq; /// /// Extensions for the NancyOptions class. /// public static class NancyOptionsExtensions { /// /// Tells the NancyMiddleware to pass through when /// response has one of the given status codes. /// /// The Nancy options. /// The HTTP status code. public static void PassThroughWhenStatusCodesAre(this NancyOptions nancyOptions, params HttpStatusCode[] httpStatusCode) { nancyOptions.PerformPassThrough = context => httpStatusCode.Any(code => context.Response.StatusCode == code); } } } ================================================ FILE: src/Nancy/PipelineItem.cs ================================================ namespace Nancy { /// /// Defines a pipeline item /// /// The type of the delegate. public class PipelineItem { /// /// Gets or sets the pipeline item name. /// /// /// The name. /// public string Name { get; protected set; } /// /// Gets or sets the delegate. /// /// /// The delegate. /// public TDelegate Delegate { get; protected set; } /// /// Initializes a new instance of the class, with /// the provided and . /// /// The name. /// The delegate. public PipelineItem(string name, TDelegate @delegate) { this.Name = name; this.Delegate = @delegate; } /// /// Performs an implicit conversion from cref="TDelegate"/> to . /// /// The action. /// /// The result of the conversion. /// public static implicit operator PipelineItem(TDelegate action) { return new PipelineItem(null, action); } /// /// Performs an implicit conversion from to . /// /// The pipeline item. /// /// The result of the conversion. /// public static implicit operator TDelegate(PipelineItem pipelineItem) { return pipelineItem.Delegate; } } } ================================================ FILE: src/Nancy/Properties/InternalsVisibleTo.cs ================================================ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Nancy.Tests")] [assembly: InternalsVisibleTo("Nancy.Hosting.Self.Tests")] ================================================ FILE: src/Nancy/Request.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; using Extensions; using Helpers; using IO; using Session; /// /// Encapsulates HTTP-request information to an Nancy application. /// [DebuggerDisplay("{DebuggerDisplay, nq}")] public class Request : IDisposable { private readonly List files = new List(); private dynamic form = new DynamicDictionary(); private IDictionary cookies; /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The HTTP data transfer method used by the client. /// The path of the requested resource, relative to the "Nancy root". This should not include the scheme, host name, or query portion of the URI. /// The HTTP protocol that was used by the client. public Request(string method, string path, string scheme) : this(method, new Url { Path = path, Scheme = scheme }) { } /// /// Initializes a new instance of the class, with /// the provided , , , /// , , and . /// /// The HTTP data transfer method used by the client. /// The of the requested resource /// The headers that was passed in by the client. /// The that represents the incoming HTTP body. /// The client's IP address /// The client's certificate when present. /// The HTTP protocol version. public Request(string method, Url url, Stream body = null, IDictionary> headers = null, string ip = null, X509Certificate certificate = null, string protocolVersion = null) { if (string.IsNullOrEmpty(method)) { throw new ArgumentOutOfRangeException("method"); } if (url == null) { throw new ArgumentNullException("url"); } if (url.Path == null) { throw new ArgumentNullException("url.Path"); } if (string.IsNullOrEmpty(url.Scheme)) { throw new ArgumentOutOfRangeException("url.Scheme"); } this.UserHostAddress = ip; this.Url = url; this.Method = method; this.Query = url.Query.AsQueryDictionary(); this.Body = body ?? RequestStream.FromStream(new MemoryStream()); this.Headers = new RequestHeaders(headers ?? new Dictionary>()); this.Session = new NullSessionProvider(); if (certificate != null) { this.ClientCertificate = certificate; } this.ProtocolVersion = protocolVersion ?? string.Empty; if (string.IsNullOrEmpty(this.Url.Path)) { this.Url.Path = "/"; } this.ParseFormData(); this.RewriteMethod(); } /// /// Gets the certificate sent by the client. /// public X509Certificate ClientCertificate { get; private set; } /// /// Gets the HTTP protocol version. /// public string ProtocolVersion { get; private set; } /// /// Gets the IP address of the client /// public string UserHostAddress { get; private set; } /// /// Gets or sets the HTTP data transfer method used by the client. /// /// The method. public string Method { get; private set; } /// /// Gets the url /// public Url Url { get; private set; } /// /// Gets the request path, relative to the base path. /// Used for route matching etc. /// public string Path { get { return this.Url.Path; } } /// /// Gets the query string data of the requested resource. /// /// A instance, containing the key/value pairs of query string data. public dynamic Query { get; set; } /// /// Gets a that can be used to read the incoming HTTP body /// /// A object representing the incoming HTTP body. public Stream Body { get; private set; } /// /// Gets the request cookies. /// public IDictionary Cookies { get { return this.cookies ?? (this.cookies = this.GetCookieData()); } } /// /// Gets the current session. /// public ISession Session { get; set; } /// /// Gets the cookie data from the request header if it exists /// /// Cookies dictionary private IDictionary GetCookieData() { var cookieDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (!this.Headers.Cookie.Any()) { return cookieDictionary; } var values = this.Headers["cookie"].First().TrimEnd(';').Split(';'); foreach (var parts in values.Select(c => c.Split(new[] { '=' }, 2))) { var cookieName = parts[0].Trim(); string cookieValue; if (parts.Length == 1) { //Cookie attribute cookieValue = string.Empty; } else { cookieValue = HttpUtility.UrlDecode(parts[1]); } cookieDictionary[cookieName] = cookieValue; } return cookieDictionary; } /// /// Gets a collection of files sent by the client- /// /// An instance, containing an instance for each uploaded file. public IEnumerable Files { get { return this.files; } } /// /// Gets the form data of the request. /// /// A instance, containing the key/value pairs of form data. /// Currently Nancy will only parse form data sent using the application/x-www-url-encoded mime-type. public dynamic Form { get { return this.form; } } /// /// Gets the HTTP headers sent by the client. /// /// An containing the name and values of the headers. /// The values are stored in an of string to be compliant with multi-value headers. public RequestHeaders Headers { get; private set; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { ((IDisposable)this.Body).Dispose(); } private void ParseFormData() { if (this.Headers.ContentType == null) { return; } var contentType = this.Headers.ContentType; if (contentType.Matches("application/x-www-form-urlencoded")) { var reader = new StreamReader(this.Body); this.form = reader.ReadToEnd().AsQueryDictionary(); this.Body.Position = 0; } if (!contentType.Matches("multipart/form-data")) { return; } var boundary = Regex.Match(contentType, @"boundary=""?(?[^\n\;\"" ]*)").Groups["token"].Value; var multipart = new HttpMultipart(this.Body, boundary); var formValues = new NameValueCollection(StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase); foreach (var httpMultipartBoundary in multipart.GetBoundaries()) { if (string.IsNullOrEmpty(httpMultipartBoundary.Filename)) { var reader = new StreamReader(httpMultipartBoundary.Value); formValues.Add(httpMultipartBoundary.Name, reader.ReadToEnd()); } else { this.files.Add(new HttpFile(httpMultipartBoundary)); } } foreach (var key in formValues.AllKeys.Where(key => key != null)) { this.form[key] = formValues[key]; } this.Body.Position = 0; } private void RewriteMethod() { if (!this.Method.Equals("POST", StringComparison.OrdinalIgnoreCase)) { return; } var overrides = new List> { Tuple.Create("_method form input element", (string)this.Form["_method"]), Tuple.Create("X-HTTP-Method-Override form input element", (string)this.Form["X-HTTP-Method-Override"]), Tuple.Create("X-HTTP-Method-Override header", this.Headers["X-HTTP-Method-Override"].FirstOrDefault()) }; var providedOverride = overrides.Where(x => !string.IsNullOrEmpty(x.Item2)) .ToList(); if (!providedOverride.Any()) { return; } if (providedOverride.Count > 1) { var overrideSources = string.Join(", ", providedOverride); var errorMessage = string.Format("More than one HTTP method override was provided. The provided values where: {0}", overrideSources); throw new InvalidOperationException(errorMessage); } this.Method = providedOverride.Single().Item2; } private string DebuggerDisplay { get { return string.Format("{0} {1} {2}", this.Method, this.Url, this.ProtocolVersion).Trim(); } } } } ================================================ FILE: src/Nancy/RequestExecutionException.cs ================================================ namespace Nancy { using System; /// /// Exception that is thrown when an unhandled exception occurred during /// the execution of the current request. /// public class RequestExecutionException : Exception { /// /// Initializes a new instance of the , with /// the specified . /// /// public RequestExecutionException(Exception innerException) : base("Oh noes!", innerException) { } } } ================================================ FILE: src/Nancy/RequestHeaders.cs ================================================ namespace Nancy { using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using Nancy.Cookies; using Nancy.Responses.Negotiation; /// /// Provides strongly-typed access to HTTP request headers. /// public class RequestHeaders : IEnumerable>> { private readonly IDictionary> headers; private readonly ConcurrentDictionary>> cache; /// /// Initializes a new instance of the class. /// /// The headers. public RequestHeaders(IDictionary> headers) { this.headers = new Dictionary>(headers, StringComparer.OrdinalIgnoreCase); this.cache = new ConcurrentDictionary>>(StringComparer.OrdinalIgnoreCase); } /// /// Content-types that are acceptable. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable> Accept { get { return this.GetWeightedValues("Accept"); } set { this.SetHeaderValues("Accept", value, GetWeightedValuesAsStrings); } } /// /// Character sets that are acceptable. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable> AcceptCharset { get { return this.GetWeightedValues("Accept-Charset"); } set { this.SetHeaderValues("Accept-Charset", value, GetWeightedValuesAsStrings); } } /// /// Acceptable encodings. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable AcceptEncoding { get { return this.GetSplitValues("Accept-Encoding"); } set { this.SetHeaderValues("Accept-Encoding", value, x => x); } } /// /// Acceptable languages for response. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable> AcceptLanguage { get { return this.GetWeightedValues("Accept-Language"); } set { this.SetHeaderValues("Accept-Language", value, GetWeightedValuesAsStrings); } } /// /// Authorization header value for request. /// /// A containing the header value if it is available; otherwise . public string Authorization { get { return this.GetValue("Authorization", x => x.First(), string.Empty); } set { this.SetHeaderValues("Authorization", value, x => new[] { x }); } } /// /// Used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable CacheControl { get { return this.GetValue("Cache-Control"); } set { this.SetHeaderValues("Cache-Control", value, x => x); } } /// /// Contains name/value pairs of information stored for that URL. /// /// An that contains instances if they are available; otherwise it will be empty. public IEnumerable Cookie { get { return this.GetValue("Cookie", GetNancyCookies, Enumerable.Empty()); } } /// /// What type of connection the user-agent would prefer. /// /// A containing the header value if it is available; otherwise . public string Connection { get { return this.GetValue("Connection", x => x.First(), string.Empty); } set { this.SetHeaderValues("Connection", value, x => new[] { x }); } } /// /// The length of the request body in octets (8-bit bytes). /// /// The length of the contents if it is available; otherwise 0. public long ContentLength { get { return this.GetValue("Content-Length", x => Convert.ToInt64(x.First()), 0); } set { this.SetHeaderValues("Content-Length", value, x => new[] { x.ToString(CultureInfo.InvariantCulture) }); } } /// /// The mime type of the body of the request (used with POST and PUT requests). /// /// A containing the header value if it is available; otherwise . public MediaRange ContentType { get { return this.GetValue("Content-Type", x => new MediaRange(x.First()), null); } set { this.SetHeaderValues("Content-Type", value, x => new[] { x.ToString() }); } } /// /// The date and time that the message was sent. /// /// A instance that specifies when the message was sent. If not available then will be returned. public DateTime? Date { get { return this.GetValue("Date", x => ParseDateTime(x.First()), null); } set { this.SetHeaderValues("Date", value, x => new[] { GetDateAsString(value) }); } } /// /// The domain name of the server (for virtual hosting), mandatory since HTTP/1.1 /// /// A containing the header value if it is available; otherwise . public string Host { get { return this.GetValue("Host", x => x.First(), string.Empty); } set { this.SetHeaderValues("Host", value, x => new[] { x }); } } /// /// Only perform the action if the client supplied entity matches the same entity on the server. This is mainly for methods like PUT to only update a resource if it has not been modified since the user last updated it. /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable IfMatch { get { return this.GetValue("If-Match"); } set { this.SetHeaderValues("If-Match", value, x => x); } } /// /// Allows a 304 Not Modified to be returned if content is unchanged /// /// A instance that specifies when the requested resource must have been changed since. If not available then will be returned. public DateTime? IfModifiedSince { get { return this.GetValue("If-Modified-Since", x => ParseDateTime(x.First()), null); } set { this.SetHeaderValues("If-Modified-Since", value, x => new[] { GetDateAsString(value) }); } } /// /// Allows a 304 Not Modified to be returned if content is unchanged /// /// An that contains the header values if they are available; otherwise it will be empty. public IEnumerable IfNoneMatch { get { return this.GetValue("If-None-Match"); } set { this.SetHeaderValues("If-None-Match", value, x => x); } } /// /// If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity. /// /// A containing the header value if it is available; otherwise . public string IfRange { get { return this.GetValue("If-Range", x => x.First(), string.Empty); } set { this.SetHeaderValues("If-Range", value, x => new[] { x }); } } /// /// Only send the response if the entity has not been modified since a specific time. /// /// A instance that specifies when the requested resource may not have been changed since. If not available then will be returned. public DateTime? IfUnmodifiedSince { get { return this.GetValue("If-Unmodified-Since", x => ParseDateTime(x.First()), null); } set { this.SetHeaderValues("If-Unmodified-Since", value, x => new[] { GetDateAsString(value) }); } } /// /// Gets the names of the available request headers. /// /// An containing the names of the headers. public IEnumerable Keys { get { return this.headers.Keys; } } /// /// Limit the number of times the message can be forwarded through proxies or gateways. /// /// The number of the maximum allowed number of forwards if it is available; otherwise 0. public int MaxForwards { get { return this.GetValue("Max-Forwards", x => Convert.ToInt32(x.First()), 0); } set { this.SetHeaderValues("Max-Forwards", value, x => new[] { x.ToString(CultureInfo.InvariantCulture) }); } } /// /// This is the address of the previous web page from which a link to the currently requested page was followed. /// /// A containing the header value if it is available; otherwise . public string Referrer { get { return this.GetValue("Referer", x => x.First(), string.Empty); } set { this.SetHeaderValues("Referer", value, x => new[] { x }); } } /// /// The user agent string of the user agent /// /// A containing the header value if it is available; otherwise . public string UserAgent { get { return this.GetValue("User-Agent", x => x.First(), string.Empty); } set { this.SetHeaderValues("User-Agent", value, x => new[] { x }); } } /// /// Gets all the header values. /// /// An that contains all the header values. public IEnumerable> Values { get { return this.headers.Values; } } /// /// Returns an enumerator that iterates through the collection. /// /// A that can be used to iterate through the collection. public IEnumerator>> GetEnumerator() { return this.headers.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// /// Gets the values for the header identified by the parameter. /// /// The name of the header to return the values for. /// An that contains the values for the header. If the header is not defined then is returned. public IEnumerable this[string name] { get { IEnumerable value; return this.headers.TryGetValue(name, out value) ? value : Enumerable.Empty(); } } private static string GetDateAsString(DateTime? value) { return !value.HasValue ? null : value.Value.ToString("R", CultureInfo.InvariantCulture); } private IEnumerable GetSplitValues(string header) { var values = this.GetValue(header); return values .SelectMany(x => x.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) .Select(x => x.Trim()) .ToList(); } private IEnumerable> GetWeightedValues(string headerName) { return this.cache.GetOrAdd(headerName, r => { var values = this.GetValue(r); var result = new List>(); foreach (var header in values) { var buffer = string.Empty; var name = string.Empty; var quality = string.Empty; var isReadingQuality = false; var isInQuotedSection = false; for (var index = 0; index < header.Length; index++) { var character = header[index]; if (character.Equals(' ') && (index != header.Length - 1) && !isInQuotedSection) { continue; } if (character.Equals('"')) { isInQuotedSection = !isInQuotedSection; } if (isInQuotedSection) { buffer += character; if (index != header.Length - 1) { continue; ; } } if (character.Equals(';') || character.Equals(',') || (index == header.Length - 1)) { if (!(character.Equals(';') || character.Equals(','))) { buffer += character; } if (isReadingQuality) { quality = buffer; } else { if (name.Length > 0) { name += ';'; } name += buffer; } buffer = string.Empty; isReadingQuality = false; isInQuotedSection = false; } if (character.Equals(';')) { continue; } if ((character.Equals('q') || character.Equals('Q')) && (index != header.Length - 1)) { if (header[index + 1].Equals('=')) { isReadingQuality = true; continue; } } if (isReadingQuality && character.Equals('=')) { continue; } if (character.Equals(',') || (index == header.Length - 1)) { var actualQuality = 1m; decimal temp; if (decimal.TryParse(quality, NumberStyles.Number, CultureInfo.InvariantCulture, out temp)) { actualQuality = temp; } result.Add(new Tuple(name, actualQuality)); name = string.Empty; quality = string.Empty; buffer = string.Empty; isReadingQuality = false; continue; } buffer += character; } } return result.OrderByDescending(x => x.Item2); }); } private static IEnumerable GetNancyCookies(IEnumerable cookies) { if (cookies == null) { yield break; } foreach (var cookie in cookies) { var cookieStrings = cookie.Split(';'); foreach (var cookieString in cookieStrings) { var equalPos = cookieString.IndexOf('='); if (equalPos >= 0) { yield return new NancyCookie(cookieString.Substring(0, equalPos).TrimStart(), cookieString.Substring(equalPos+1).TrimEnd()); } } } } private IEnumerable GetValue(string name) { return this.GetValue(name, x => x, new string[] {}); } private T GetValue(string name, Func, T> converter, T defaultValue) { IEnumerable values; if (!this.headers.TryGetValue(name, out values)) { return defaultValue; } return converter.Invoke(values); } private static IEnumerable GetWeightedValuesAsStrings(IEnumerable> values) { return values.Select(x => string.Concat(x.Item1, ";q=", x.Item2.ToString(CultureInfo.InvariantCulture))); } private static DateTime? ParseDateTime(string value) { DateTime result; // note CultureInfo.InvariantCulture is ignored if (DateTime.TryParseExact(value, "R", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)) { return result; } return null; } private void SetHeaderValues(string header, T value, Func> valueTransformer) { this.InvalidateCacheEntry(header); if (EqualityComparer.Default.Equals(value, default(T))) { if (this.headers.ContainsKey(header)) { this.headers.Remove(header); } } else { this.headers[header] = valueTransformer.Invoke(value); } } private void InvalidateCacheEntry(string header) { IEnumerable> values; this.cache.TryRemove(header, out values); } } } ================================================ FILE: src/Nancy/ResourceAssemblyProvider.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; /// /// Default set of assemblies that should be scanned for items (views, text, content etc) /// embedded as resources. /// /// The default convention will scan all assemblies that references another assemblies that has a name that starts with Nancy* public class ResourceAssemblyProvider : IResourceAssemblyProvider { private readonly IAssemblyCatalog assemblyCatalog; private IEnumerable filteredAssemblies; /// /// Initializes a new instance of the /// /// An instance. public ResourceAssemblyProvider(IAssemblyCatalog assemblyCatalog) { this.assemblyCatalog = assemblyCatalog; } /// /// Gets a list of assemblies that should be scanned for views. /// /// An of instances. public IEnumerable GetAssembliesToScan() { return (this.filteredAssemblies ?? (this.filteredAssemblies = this.GetFilteredAssemblies())); } private IEnumerable GetFilteredAssemblies() { return this.assemblyCatalog .GetAssemblies() .Where(x => !x.GetName().Name.StartsWith("Nancy", StringComparison.OrdinalIgnoreCase)); } } } ================================================ FILE: src/Nancy/Response.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using Nancy.Cookies; using Nancy.Helpers; using Nancy.Responses; /// /// Encapsulates HTTP-response information from an Nancy operation. /// [DebuggerDisplay("{DebuggerDisplay, nq}")] public class Response: IDisposable { /// /// Null object representing no body /// public static Action NoBody = s => { }; private string contentType; /// /// Initializes a new instance of the class. /// public Response() { this.Contents = NoBody; this.ContentType = "text/html"; this.Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); this.StatusCode = HttpStatusCode.OK; this.Cookies = new List(2); } /// /// Gets or sets the type of the content. /// /// The type of the content. /// The default value is text/html. public string ContentType { get { string value; return Headers.TryGetValue("content-type", out value) ? value : this.contentType; } set { this.contentType = value; } } /// /// Gets the delegate that will render contents to the response stream. /// /// An delegate, containing the code that will render contents to the response stream. /// The host of Nancy will pass in the output stream after the response has been handed back to it by Nancy. public Action Contents { get; set; } /// /// Gets the collection of HTTP response headers that should be sent back to the client. /// /// An instance, containing the key/value pair of headers. public IDictionary Headers { get; set; } /// /// Gets or sets the HTTP status code that should be sent back to the client. /// /// A value. public HttpStatusCode StatusCode { get; set; } /// /// Gets or sets a text description of the HTTP status code returned to the client. /// /// The HTTP status code description. public string ReasonPhrase { get; set; } /// /// Gets the instances that are associated with the response. /// /// A instance, containing instances. public IList Cookies { get; private set; } /// /// Executes at the end of the nancy execution pipeline and before control is passed back to the hosting. /// Can be used to pre-render/validate views while still inside the main pipeline/error handling. /// /// Nancy context /// Task for completion/erroring public virtual Task PreExecute(NancyContext context) { return TaskHelpers.CompletedTask; } /// /// Implicitly cast an value to a instance, with the /// set to the value of the . /// /// The value that is being cast from. /// A instance. public static implicit operator Response(HttpStatusCode statusCode) { return new Response { StatusCode = statusCode }; } /// /// Implicitly cast an int value to a instance, with the /// set to the value of the int. /// /// The int value that is being cast from. /// A instance. public static implicit operator Response(int statusCode) { return new Response { StatusCode = (HttpStatusCode)statusCode }; } /// /// Implicitly cast an string instance to a instance, with the /// set to the value of the string. /// /// The string that is being cast from. /// A instance. public static implicit operator Response(string contents) { return new TextResponse(contents); } /// /// Implicitly cast an , where T is a , instance to /// a instance, with the set to the value of the action. /// /// The instance that is being cast from. /// A instance. public static implicit operator Response(Action streamFactory) { return new Response { Contents = streamFactory }; } /// /// Implicitly cast a instance to a instance, /// with the set to the value of the . /// /// The instance that is being cast from. /// A instance. public static implicit operator Response(DynamicDictionaryValue value) { return new Response { Contents = GetStringContents(value) }; } /// /// Converts a string content value to a response action. /// /// The string containing the content. /// A response action that will write the content of the string to the response stream. protected static Action GetStringContents(string contents) { return stream => { var writer = new StreamWriter(stream) { AutoFlush = true }; writer.Write(contents); }; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// This method can be overridden in sub-classes to dispose of response specific resources. public virtual void Dispose() { } private string DebuggerDisplay { get { return string.Join(" ", new string[] { this.StatusCode.ToString(), this.ReasonPhrase, this.ContentType }.Where(x => !string.IsNullOrEmpty(x)).ToArray()); } } } } ================================================ FILE: src/Nancy/ResponseExtensions.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Nancy.Cookies; using Nancy.Responses; /// /// Containing extensions for the object. /// public static class ResponseExtensions { /// /// Force the response to be downloaded as an attachment /// /// Response object /// Filename for the download /// Optional content type /// Modified Response object public static Response AsAttachment(this Response response, string fileName = null, string contentType = null) { var actualFilename = fileName; if (actualFilename == null && response is GenericFileResponse) { actualFilename = ((GenericFileResponse)response).Filename; } if (string.IsNullOrEmpty(actualFilename)) { throw new ArgumentException("fileName cannot be null or empty"); } if (contentType != null) { response.ContentType = contentType; } return response.WithHeader("Content-Disposition", "attachment; filename=" + actualFilename); } /// /// Adds a to the response. /// /// Response object /// The name of the cookie. /// The value of the cookie. /// The instance. public static Response WithCookie(this Response response, string name, string value) { return WithCookie(response, name, value, null, null, null); } /// /// Adds a to the response. /// /// Response object /// The name of the cookie. /// The value of the cookie. /// The expiration date of the cookie. Can be if it should expire at the end of the session. /// The instance. public static Response WithCookie(this Response response, string name, string value, DateTime? expires) { return WithCookie(response, name, value, expires, null, null); } /// /// Adds a to the response. /// /// Response object /// The name of the cookie. /// The value of the cookie. /// The expiration date of the cookie. Can be if it should expire at the end of the session. /// The domain of the cookie. /// The path of the cookie. /// The instance. public static Response WithCookie(this Response response, string name, string value, DateTime? expires, string domain, string path) { return WithCookie(response, new NancyCookie(name, value) { Expires = expires, Domain = domain, Path = path }); } /// /// Adds a to the response. /// /// Response object /// A instance. /// public static Response WithCookie(this Response response, INancyCookie nancyCookie) { response.Cookies.Add(nancyCookie); return response; } /// /// Add a header to the response /// /// Response object /// Header name /// Header value /// Modified response public static Response WithHeader(this Response response, string header, string value) { return response.WithHeaders(new { Header = header, Value = value }); } /// /// Adds headers to the response using anonymous types /// /// Response object /// /// Array of headers - each header should be an anonymous type with two string properties /// 'Header' and 'Value' to represent the header name and its value. /// /// Modified response public static Response WithHeaders(this Response response, params object[] headers) { return response.WithHeaders(headers.Select(GetTuple).ToArray()); } /// /// Adds headers to the response using anonymous types /// /// Response object /// /// Array of headers - each header should be a Tuple with two string elements /// for header name and header value /// /// Modified response public static Response WithHeaders(this Response response, params Tuple[] headers) { if (response.Headers == null) { response.Headers = new Dictionary(); } foreach (var keyValuePair in headers) { response.Headers[keyValuePair.Item1] = keyValuePair.Item2; } return response; } /// /// Sets the content type of the response /// /// Response object /// The type of the content /// Modified response public static Response WithContentType(this Response response, string contentType) { response.ContentType = contentType; return response; } /// /// Sets the status code of the response /// /// Response object /// The http status code /// Modified response public static Response WithStatusCode(this Response response, HttpStatusCode statusCode) { response.StatusCode = statusCode; return response; } /// /// Sets the status code of the response /// /// Response object /// The http status code /// Modified response public static Response WithStatusCode(this Response response, int statusCode) { response.StatusCode = (HttpStatusCode) statusCode; return response; } private static Tuple GetTuple(object header) { var properties = header.GetType() .GetTypeInfo() .DeclaredProperties .Where(prop => prop.CanRead && prop.PropertyType == typeof(string)) .ToArray(); var headerProperty = properties .Where(p => string.Equals(p.Name, "Header", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); var valueProperty = properties .Where(p => string.Equals(p.Name, "Value", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); if (headerProperty == null || valueProperty == null) { throw new ArgumentException("Unable to extract 'Header' or 'Value' properties from anonymous type."); } return Tuple.Create( (string)headerProperty.GetValue(header, null), (string)valueProperty.GetValue(header, null)); } } } ================================================ FILE: src/Nancy/Responses/DefaultJsonSerializer.cs ================================================ namespace Nancy.Responses { using System; using System.Collections.Generic; using System.IO; using Nancy.Configuration; using Nancy.IO; using Nancy.Json; using Nancy.Responses.Negotiation; /// /// Default implementation for JSON serialization. /// public class DefaultJsonSerializer : ISerializer { private readonly JsonConfiguration jsonConfiguration; private readonly TraceConfiguration traceConfiguration; private readonly GlobalizationConfiguration globalizationConfiguration; /// /// Initializes a new instance of the class, /// with the provided . /// /// An instance. public DefaultJsonSerializer(INancyEnvironment environment) { this.jsonConfiguration = environment.GetValue(); this.traceConfiguration = environment.GetValue(); this.globalizationConfiguration = environment.GetValue(); } /// /// Whether the serializer can serialize the content type /// /// Content type to serialise /// True if supported, false otherwise public bool CanSerialize(MediaRange mediaRange) { return Json.IsJsonContentType(mediaRange); } /// /// Gets the list of extensions that the serializer can handle. /// /// An of extensions if any are available, otherwise an empty enumerable. public IEnumerable Extensions { get { yield return "json"; } } /// /// Serialize the given model with the given contentType /// /// Content type to serialize into /// Model to serialize /// Stream to serialize to /// Serialised object public void Serialize(MediaRange mediaRange, TModel model, Stream outputStream) { using (var writer = new StreamWriter(new UnclosableStreamWrapper(outputStream))) { var serializer = new JavaScriptSerializer(this.jsonConfiguration, this.globalizationConfiguration); serializer.RegisterConverters(this.jsonConfiguration.Converters, this.jsonConfiguration.PrimitiveConverters); try { serializer.Serialize(model, writer); } catch (Exception exception) { if (this.traceConfiguration.DisplayErrorTraces) { writer.Write(exception.Message); } } } } } } ================================================ FILE: src/Nancy/Responses/DefaultXmlSerializer.cs ================================================ namespace Nancy.Responses { using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Xml.Serialization; using Nancy.Configuration; using Nancy.Responses.Negotiation; using Nancy.Xml; /// /// Default implementation for XML serialization. /// public class DefaultXmlSerializer : ISerializer { private readonly XmlConfiguration configuration; private readonly TraceConfiguration traceConfiguration; /// /// Initializes a new instance of the class, /// with the provided . /// /// An instance. public DefaultXmlSerializer(INancyEnvironment environment) { this.configuration = environment.GetValue(); this.traceConfiguration = environment.GetValue(); } /// /// Whether the serializer can serialize the content type /// /// Content type to serialise /// True if supported, false otherwise public bool CanSerialize(MediaRange mediaRange) { return IsXmlType(mediaRange); } /// /// Gets the list of extensions that the serializer can handle. /// /// An of extensions if any are available, otherwise an empty enumerable. public IEnumerable Extensions { get { yield return "xml"; } } /// /// Serialize the given model with the given contentType /// /// Content type to serialize into /// Model to serialize /// Output stream to serialize to /// Serialised object public void Serialize(MediaRange mediaRange, TModel model, Stream outputStream) { try { var serializer = new XmlSerializer(typeof(TModel)); if (this.configuration.EncodingEnabled) { serializer.Serialize(new StreamWriter(outputStream, this.configuration.DefaultEncoding), model); } else { serializer.Serialize(outputStream, model); } } catch (Exception exception) { if (this.traceConfiguration.DisplayErrorTraces) { var bytes = Encoding.UTF8.GetBytes(exception.Message); outputStream.Write(bytes, 0, exception.Message.Length); } } } private static bool IsXmlType(string contentType) { if (string.IsNullOrEmpty(contentType)) { return false; } var contentMimeType = contentType.Split(';')[0]; return contentMimeType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase) || contentMimeType.Equals("text/xml", StringComparison.OrdinalIgnoreCase) || contentMimeType.EndsWith("+xml", StringComparison.OrdinalIgnoreCase); } } } ================================================ FILE: src/Nancy/Responses/EmbeddedFileResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; using System.Linq; using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; /// /// Represent an HTML response with embeded file content. /// /// public class EmbeddedFileResponse : Response { private static readonly byte[] ErrorText; static EmbeddedFileResponse() { ErrorText = Encoding.UTF8.GetBytes("NOT FOUND"); } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The assembly. /// The resource path. /// The name. public EmbeddedFileResponse(Assembly assembly, string resourcePath, string name) { this.ContentType = MimeTypes.GetMimeType(name); this.StatusCode = HttpStatusCode.OK; var content = GetResourceContent(assembly, resourcePath, name); if (content != null) { this.WithHeader("ETag", GenerateETag(content)); content.Seek(0, SeekOrigin.Begin); } this.Contents = stream => { if (content != null) { content.CopyTo(stream); } else { stream.Write(ErrorText, 0, ErrorText.Length); } }; } private Stream GetResourceContent(Assembly assembly, string resourcePath, string name) { var resourceName = assembly .GetManifestResourceNames() .FirstOrDefault(x => GetFileNameFromResourceName(resourcePath, x).Equals(name, StringComparison.OrdinalIgnoreCase)); if (resourceName == null) return null; return assembly.GetManifestResourceStream(resourceName); } private static string GetFileNameFromResourceName(string resourcePath, string resourceName) { return Regex.Replace(resourceName, resourcePath, string.Empty, RegexOptions.IgnoreCase).Substring(1); } private static string GenerateETag(Stream stream) { using (var sha1 = SHA1.Create()) { var hash = sha1.ComputeHash(stream); return string.Concat("\"", ByteArrayToString(hash), "\""); } } private static string ByteArrayToString(byte[] data) { var output = new StringBuilder(data.Length); for (int i = 0; i < data.Length; i++) { output.Append(data[i].ToString("X2")); } return output.ToString(); } } } ================================================ FILE: src/Nancy/Responses/GenericFileResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; using System.Linq; using Nancy.Configuration; using Nancy.Helpers; /// /// A response representing a file. /// /// If the response contains an invalid file (not found, empty name, missing extension and so on) the status code of the response will be set to . public class GenericFileResponse : Response { private readonly StaticContentConfiguration configuration; /// /// Size of buffer for transmitting file. Default size 4 Mb /// public static int BufferSize = 4 * 1024 * 1024; /// /// Initializes a new instance of the for the file specified /// by the parameter and . /// /// The name of the file, including path relative to the root of the application, that should be returned. /// The method will be used to determine the mimetype of the file and will be used as the content-type of the response. If no match if found the content-type will be set to application/octet-stream. /// Current context public GenericFileResponse(string filePath, NancyContext context) : this(filePath, MimeTypes.GetMimeType(filePath), context) { } /// /// Initializes a new instance of the for the file specified /// by the parameter, the content-type specified by the parameter /// and . /// /// The name of the file, including path relative to the root of the application, that should be returned. /// The content-type of the response. /// Current context public GenericFileResponse(string filePath, string contentType, NancyContext context) { var environment = context.Environment; this.configuration = environment.GetValue(); this.InitializeGenericFileResponse(filePath, contentType, context); } /// /// Gets the filename of the file response /// /// A string containing the name of the file. public string Filename { get; protected set; } private static Action GetFileContent(string filePath, long length) { return stream => { using (var file = File.OpenRead(filePath)) { file.CopyTo(stream, (int)(length < BufferSize ? length : BufferSize)); } }; } static bool IsSafeFilePath(string rootPath, string filePath) { if (!File.Exists(filePath)) { return false; } var fullPath = Path.GetFullPath(filePath); return fullPath.StartsWith(Path.GetFullPath(rootPath), StringComparison.OrdinalIgnoreCase); } private void InitializeGenericFileResponse(string filePath, string contentType, NancyContext context) { if (string.IsNullOrEmpty(filePath)) { StatusCode = HttpStatusCode.NotFound; return; } if (this.configuration.SafePaths == null || !this.configuration.SafePaths.Any()) { throw new InvalidOperationException("No SafePaths defined."); } foreach (var rootPath in this.configuration.SafePaths) { string fullPath; if (Path.IsPathRooted(filePath)) { fullPath = filePath; } else { fullPath = Path.Combine(rootPath, filePath); } if (IsSafeFilePath(rootPath, fullPath)) { this.Filename = Path.GetFileName(fullPath); this.SetResponseValues(contentType, fullPath, context); return; } } StatusCode = HttpStatusCode.NotFound; } private void SetResponseValues(string contentType, string fullPath, NancyContext context) { // TODO - set a standard caching time and/or public? var fi = new FileInfo(fullPath); var lastWriteTimeUtc = fi.LastWriteTimeUtc; var etag = string.Concat("\"", lastWriteTimeUtc.Ticks.ToString("x"), "\""); var lastModified = lastWriteTimeUtc.ToString("R"); var length = fi.Length; if (CacheHelpers.ReturnNotModified(etag, lastWriteTimeUtc, context)) { this.StatusCode = HttpStatusCode.NotModified; this.ContentType = null; this.Contents = NoBody; return; } this.Headers["ETag"] = etag; this.Headers["Last-Modified"] = lastModified; this.Headers["Content-Length"] = length.ToString(); if (length > 0) { this.Contents = GetFileContent(fullPath, length); } this.ContentType = contentType; this.StatusCode = HttpStatusCode.OK; } } } ================================================ FILE: src/Nancy/Responses/HtmlResponse.cs ================================================ namespace Nancy.Responses { using System; using System.Collections.Generic; using System.IO; using Nancy.Cookies; /// /// Represents a HTML (text/html) response /// public class HtmlResponse : Response { /// /// Creates a new instance of the class, with /// the provided , , /// and /// /// Status code - defaults to OK /// Response body delegate - defaults to empty if null /// Headers if required /// Cookies if required public HtmlResponse(HttpStatusCode statusCode = HttpStatusCode.OK, Action contents = null, IDictionary headers = null, IEnumerable cookies = null) { this.ContentType = "text/html"; this.StatusCode = statusCode; if (contents != null) { this.Contents = contents; } if (headers != null) { this.Headers = headers; } if (cookies != null) { foreach (var nancyCookie in cookies) { this.Cookies.Add(nancyCookie); } } } } } ================================================ FILE: src/Nancy/Responses/JsonResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; using Nancy.Configuration; using Nancy.Json; /// /// Represents a JSON response of the type . /// /// The type of the model. public class JsonResponse : Response { private readonly JsonConfiguration configuration; /// /// Initializes a new instance of the class, /// with the provided , /// and . /// /// The model that should be returned as JSON. /// The to use for the serialization. /// An instance. public JsonResponse(TModel model, ISerializer serializer, INancyEnvironment environment) { if (serializer == null) { throw new InvalidOperationException("JSON Serializer not set"); } this.configuration = environment.GetValue(); this.Contents = model == null ? NoBody : this.GetJsonContents(model, serializer); this.ContentType = this.DefaultContentType; this.StatusCode = HttpStatusCode.OK; } private string DefaultContentType { get { return string.Concat("application/json", this.Encoding); } } private string Encoding { get { return string.Concat("; charset=", this.configuration.DefaultEncoding.WebName); } } private Action GetJsonContents(TModel model, ISerializer serializer) { return stream => serializer.Serialize(this.DefaultContentType, model, stream); } } /// /// Represents a JSON response /// public class JsonResponse : JsonResponse { /// /// Initializes a new instance of the class, /// with the provided , /// and . /// /// The model that should be returned as JSON. /// The to use for the serialization. /// An instance. public JsonResponse(object model, ISerializer serializer, INancyEnvironment environment) : base(model, serializer, environment) { } } } ================================================ FILE: src/Nancy/Responses/MaterialisingResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; using System.Threading.Tasks; using Nancy.Helpers; /// /// Takes an existing response and materialises the body. /// Can be used as a wrapper to force execution of the deferred body for /// error checking etc. /// Copies the existing response into memory, so use with caution. /// public class MaterialisingResponse : Response { private readonly Response sourceResponse; private byte[] oldResponseOutput; /// /// Executes at the end of the nancy execution pipeline and before control is passed back to the hosting. /// Can be used to pre-render/validate views while still inside the main pipeline/error handling. /// /// Nancy context /// /// Task for completion/erroring /// public override Task PreExecute(NancyContext context) { using (var memoryStream = new MemoryStream()) { this.sourceResponse.Contents.Invoke(memoryStream); this.oldResponseOutput = memoryStream.ToArray(); } return base.PreExecute(context); } /// /// Initializes a new instance of the class, with /// the provided . /// /// The source response. public MaterialisingResponse(Response sourceResponse) { this.sourceResponse = sourceResponse; this.ContentType = sourceResponse.ContentType; this.Headers = sourceResponse.Headers; this.StatusCode = sourceResponse.StatusCode; this.ReasonPhrase = sourceResponse.ReasonPhrase; this.Contents = WriteContents; } private void WriteContents(Stream stream) { if (this.oldResponseOutput == null) { this.sourceResponse.Contents.Invoke(stream); } else { stream.Write(this.oldResponseOutput, 0, this.oldResponseOutput.Length); } } } } ================================================ FILE: src/Nancy/Responses/NegotiatedResponse.cs ================================================ namespace Nancy.Responses { /// /// Response that indicates that the response format should be negotiated between the client and the server. /// public class NegotiatedResponse : Response { /// /// Initializes a new instance of the response for the /// provided . /// /// The response value that should be negotiated. public NegotiatedResponse(dynamic value) { Value = value; } /// /// Gets or sets the value that should be negotiated. /// public dynamic Value { get; set; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/DefaultResponseNegotiator.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Nancy.Conventions; using Nancy.Extensions; using Nancy.Helpers; /// /// The default implementation for a response negotiator. /// public class DefaultResponseNegotiator : IResponseNegotiator { private readonly IReadOnlyCollection processors; private readonly AcceptHeaderCoercionConventions coercionConventions; /// /// Initializes a new instance of the class. /// /// The response processors. /// The Accept header coercion conventions. public DefaultResponseNegotiator(IEnumerable processors, AcceptHeaderCoercionConventions coercionConventions) { this.processors = processors.ToArray(); this.coercionConventions = coercionConventions; } /// /// Negotiates the response based on the given result and context. /// /// The route result. /// The context. /// A . public Response NegotiateResponse(dynamic routeResult, NancyContext context) { Response response; if (TryCastResultToResponse(routeResult, out response)) { context.WriteTraceLog(sb => sb.AppendLine("[DefaultResponseNegotiator] Processing as real response")); return response; } context.WriteTraceLog(sb => sb.AppendLine("[DefaultResponseNegotiator] Processing as negotiation")); NegotiationContext negotiationContext = GetNegotiationContext(routeResult, context); var coercedAcceptHeaders = this.GetCoercedAcceptHeaders(context).ToArray(); context.WriteTraceLog(sb => GetAccepHeaderTraceLog(context, negotiationContext, coercedAcceptHeaders, sb)); var compatibleHeaders = this.GetCompatibleHeaders(coercedAcceptHeaders, negotiationContext, context).ToArray(); if (!compatibleHeaders.Any()) { context.WriteTraceLog(sb => sb.AppendLine("[DefaultResponseNegotiator] Unable to negotiate response - no headers compatible")); return new NotAcceptableResponse(); } return CreateResponse(compatibleHeaders, negotiationContext, context); } /// /// Tries to cast the dynamic result to a . /// /// The result. /// The response. /// true if the result is a , false otherwise. private static bool TryCastResultToResponse(dynamic routeResult, out Response response) { var targetType = routeResult.GetType(); var responseType = typeof(Response); if (routeResult is Response) { response = (Response)routeResult; return true; } var methods = responseType.GetMethods(BindingFlags.Public | BindingFlags.Static); foreach (var method in methods) { if (!method.Name.Equals("op_Implicit", StringComparison.Ordinal)) { continue; } if (method.ReturnType != responseType) { continue; } var parameters = method.GetParameters(); if (parameters.Length != 1) { continue; } if (parameters[0].ParameterType != targetType) { continue; } response = (Response)routeResult; return true; } response = null; return false; } /// /// Gets a based on the given result and context. /// /// The route result. /// The context. /// A . private static NegotiationContext GetNegotiationContext(object routeResult, NancyContext context) { var negotiator = routeResult as Negotiator; if (negotiator == null) { context.WriteTraceLog(sb => sb.AppendFormat("[DefaultResponseNegotiator] Wrapping result of type {0} in negotiator\n", routeResult.GetType())); negotiator = new Negotiator(context).WithModel(routeResult); } return negotiator.NegotiationContext; } /// /// Gets the coerced accept headers based on the . /// /// The context. /// IEnumerable{Tuple{System.String, System.Decimal}}. private IEnumerable> GetCoercedAcceptHeaders(NancyContext context) { return this.coercionConventions.Aggregate(context.Request.Headers.Accept, (current, coercion) => coercion.Invoke(current, context)); } private static void GetAccepHeaderTraceLog( NancyContext context, NegotiationContext negotiationContext, Tuple[] coercedAcceptHeaders, StringBuilder sb) { var allowableFormats = negotiationContext.PermissableMediaRanges .Select(mr => mr.ToString()) .Aggregate((t1, t2) => t1 + ", " + t2); var originalAccept = context.Request.Headers["accept"].Any() ? string.Join(", ", context.Request.Headers["accept"]) : "None"; var coercedAccept = coercedAcceptHeaders.Any() ? coercedAcceptHeaders.Select(h => h.Item1).Aggregate((t1, t2) => t1 + ", " + t2) : "None"; sb.AppendFormat("[DefaultResponseNegotiator] Original accept header: {0}\n", originalAccept); sb.AppendFormat("[DefaultResponseNegotiator] Coerced accept header: {0}\n", coercedAccept); sb.AppendFormat("[DefaultResponseNegotiator] Acceptable media ranges: {0}\n", allowableFormats); } private IEnumerable GetCompatibleHeaders( IEnumerable> coercedAcceptHeaders, NegotiationContext negotiationContext, NancyContext context) { var acceptHeaders = GetCompatibleHeaders(coercedAcceptHeaders, negotiationContext); foreach (var header in acceptHeaders) { var mediaRangeModel = negotiationContext.GetModelForMediaRange(header.Item1); IEnumerable> compatibleProcessors = this.GetCompatibleProcessorsByHeader(header.Item1, mediaRangeModel, context); if (compatibleProcessors.Any()) { yield return new CompatibleHeader(header.Item1, compatibleProcessors); } } } private static IEnumerable> GetCompatibleHeaders( IEnumerable> coercedAcceptHeaders, NegotiationContext negotiationContext) { var permissableMediaRanges = negotiationContext.PermissableMediaRanges; if (permissableMediaRanges.Any(mr => mr.IsWildcard)) { return coercedAcceptHeaders.Where(header => header.Item2 > 0m); } return coercedAcceptHeaders .Where(header => header.Item2 > 0m) .SelectMany(header => permissableMediaRanges .Where(mr => mr.Matches(header.Item1)) .Select(mr => Tuple.Create(mr.ToString(), header.Item2))); } /// /// Gets compatible response processors by header. /// /// The accept header. /// The model. /// The context. /// IEnumerable{Tuple{IResponseProcessor, ProcessorMatch}}. private IEnumerable> GetCompatibleProcessorsByHeader( string acceptHeader, dynamic model, NancyContext context) { foreach (var processor in this.processors) { ProcessorMatch match = processor.CanProcess(acceptHeader, model, context); if (match.ModelResult != MatchResult.NoMatch && match.RequestedContentTypeResult != MatchResult.NoMatch) { yield return new Tuple(processor, match); } } } /// /// Creates a response from the compatible headers. /// /// The compatible headers. /// The negotiation context. /// The context. /// A . private Response CreateResponse(IList compatibleHeaders, NegotiationContext negotiationContext, NancyContext context) { var response = NegotiateResponse(compatibleHeaders, negotiationContext, context); if (response == null) { context.WriteTraceLog(sb => sb.AppendLine("[DefaultResponseNegotiator] Unable to negotiate response - no processors returned valid response")); response = new NotAcceptableResponse(); } response.WithHeader("Vary", "Accept"); this.AddLinkHeader(compatibleHeaders, response, context.Request.Url); SetStatusCode(negotiationContext, response); SetReasonPhrase(negotiationContext, response); AddCookies(negotiationContext, response); if (response is NotAcceptableResponse) { return response; } AddContentTypeHeader(negotiationContext, response); AddNegotiatedHeaders(negotiationContext, response); return response; } /// /// Prioritizes the response processors and tries to negotiate a response. /// /// The compatible headers. /// The negotiation context. /// The context. /// Response. private static Response NegotiateResponse( IEnumerable compatibleHeaders, NegotiationContext negotiationContext, NancyContext context) { foreach (var compatibleHeader in compatibleHeaders) { var prioritizedProcessors = compatibleHeader.Processors .OrderByDescending(x => x.Item2.ModelResult) .ThenByDescending(x => x.Item2.RequestedContentTypeResult); foreach (var prioritizedProcessor in prioritizedProcessors) { var processorType = prioritizedProcessor.Item1.GetType(); context.WriteTraceLog(sb => sb.AppendFormat("[DefaultResponseNegotiator] Invoking processor: {0}\n", processorType)); var mediaRangeModel = negotiationContext.GetModelForMediaRange(compatibleHeader.MediaRange); var response = prioritizedProcessor.Item1.Process(compatibleHeader.MediaRange, mediaRangeModel, context); if (response != null) { return response; } } } return null; } /// /// Adds a link header to the . /// /// The compatible headers. /// The response. /// The request URL. private void AddLinkHeader(IEnumerable compatibleHeaders, Response response, Url requestUrl) { var linkProcessors = GetLinkProcessors(compatibleHeaders, response.ContentType); if (linkProcessors.Any()) { string existingLinkHeader; response.Headers.TryGetValue("Link", out existingLinkHeader); response.Headers["Link"] = this.CreateLinkHeader(requestUrl, linkProcessors, existingLinkHeader); } } /// /// Gets the link processors based on the compatible headers and content-type. /// /// The compatible headers. /// The content-type of the response. /// Dictionary{System.String, MediaRange}. private static IDictionary GetLinkProcessors( IEnumerable compatibleHeaders, string contentType) { var linkProcessors = new Dictionary(); foreach (var header in compatibleHeaders) { foreach (var processor in header.Processors) { foreach (var mapping in processor.Item1.ExtensionMappings) { if (!mapping.Item2.Matches(contentType)) { linkProcessors[mapping.Item1] = mapping.Item2; } } } } return linkProcessors; } /// /// Creates the link header with the different media ranges. /// /// The request URL. /// The link processors. /// The existing Link HTTP Header. /// The link header. protected virtual string CreateLinkHeader(Url requestUrl, IEnumerable> linkProcessors, string existingLinkHeader) { var fileName = HttpUtility.UrlEncode(Path.GetFileNameWithoutExtension(requestUrl.Path)); var baseUrl = string.Concat(requestUrl.BasePath, "/", fileName); var linkBuilder = new HttpLinkBuilder(); if (existingLinkHeader != null) { linkBuilder.Add(existingLinkHeader); } foreach (var linkProcessor in linkProcessors) { var uri = string.Concat(baseUrl, '.', linkProcessor.Key); linkBuilder.Add(new HttpLink(uri, "alternate", linkProcessor.Value)); } return linkBuilder.ToString(); } /// /// Adds the content type header from the to the . /// /// The negotiation context. /// The response. private static void AddContentTypeHeader(NegotiationContext negotiationContext, Response response) { string contentType; if (negotiationContext.Headers.TryGetValue("Content-Type", out contentType)) { response.ContentType = contentType; negotiationContext.Headers.Remove("Content-Type"); } } /// /// Adds the negotiated headers from the to the . /// /// The negotiation context. /// The response. private static void AddNegotiatedHeaders(NegotiationContext negotiationContext, Response response) { foreach (var header in negotiationContext.Headers) { response.Headers[header.Key] = header.Value; } } /// /// Sets the status code from the on the . /// /// The negotiation context. /// The response. private static void SetStatusCode(NegotiationContext negotiationContext, Response response) { if (negotiationContext.StatusCode.HasValue) { response.StatusCode = negotiationContext.StatusCode.Value; } } /// /// Sets the reason phrase from the on the . /// /// The negotiation context. /// The response. private static void SetReasonPhrase(NegotiationContext negotiationContext, Response response) { if (negotiationContext.ReasonPhrase != null) { response.ReasonPhrase = negotiationContext.ReasonPhrase; } } /// /// Adds the cookies from the to the . /// /// The negotiation context. /// The response. private static void AddCookies(NegotiationContext negotiationContext, Response response) { foreach (var cookie in negotiationContext.Cookies) { response.Cookies.Add(cookie); } } private class CompatibleHeader { public CompatibleHeader( string mediaRange, IEnumerable> processors) { this.MediaRange = mediaRange; this.Processors = processors; } public string MediaRange { get; private set; } public IEnumerable> Processors { get; private set; } } } } ================================================ FILE: src/Nancy/Responses/Negotiation/IResponseNegotiator.cs ================================================ namespace Nancy.Responses.Negotiation { /// /// Creates a response from a given result and context. /// public interface IResponseNegotiator { /// /// Negotiates the response based on the given result and context. /// /// The route result. /// The context. /// A . Response NegotiateResponse(dynamic routeResult, NancyContext context); } } ================================================ FILE: src/Nancy/Responses/Negotiation/IResponseProcessor.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; /// /// Content negotiation response processor /// public interface IResponseProcessor { /// /// Gets a set of mappings that map a given extension (such as .json) /// to a media range that can be sent to the client in a vary header. /// IEnumerable> ExtensionMappings { get; } /// /// Determines whether the processor can handle a given content type and model. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A result that determines the priority of the processor. ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context); /// /// Process the response. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A instance. Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context); } } ================================================ FILE: src/Nancy/Responses/Negotiation/JsonProcessor.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using System.Linq; using Nancy.Configuration; /// /// Processes the model for json media types and extension. /// public class JsonProcessor : IResponseProcessor { private readonly ISerializer serializer; private readonly INancyEnvironment environment; private static readonly IEnumerable> extensionMappings = new[] { new Tuple("json", new MediaRange("application/json")) }; /// /// Initializes a new instance of the class, /// with the provided . /// /// The serializes that the processor will use to process the request. /// An instance. public JsonProcessor(IEnumerable serializers, INancyEnvironment environment) { this.serializer = serializers.FirstOrDefault(x => x.CanSerialize("application/json")); this.environment = environment; } /// /// Gets a set of mappings that map a given extension (such as .json) /// to a media range that can be sent to the client in a vary header. /// public IEnumerable> ExtensionMappings { get { return extensionMappings; } } /// /// Determines whether the processor can handle a given content type and model /// /// Content type requested by the client /// The model for the given media range /// The nancy context /// A ProcessorMatch result that determines the priority of the processor public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context) { if (IsExactJsonContentType(requestedMediaRange)) { return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.ExactMatch }; } if (IsWildcardJsonContentType(requestedMediaRange)) { return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.NonExactMatch }; } return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.NoMatch }; } /// /// Process the response /// /// Content type requested by the client /// The model for the given media range /// The nancy context /// A response public Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context) { return new JsonResponse(model, this.serializer, this.environment); } private static bool IsExactJsonContentType(MediaRange requestedContentType) { if (requestedContentType.Type.IsWildcard && requestedContentType.Subtype.IsWildcard) { return true; } return requestedContentType.Matches("application/json") || requestedContentType.Matches("text/json"); } private static bool IsWildcardJsonContentType(MediaRange requestedContentType) { if (!requestedContentType.Type.IsWildcard && !string.Equals("application", requestedContentType.Type, StringComparison.OrdinalIgnoreCase)) { return false; } if (requestedContentType.Subtype.IsWildcard) { return true; } var subtypeString = requestedContentType.Subtype.ToString(); return subtypeString.EndsWith("+json", StringComparison.OrdinalIgnoreCase); } } } ================================================ FILE: src/Nancy/Responses/Negotiation/MatchResult.cs ================================================ namespace Nancy.Responses.Negotiation { /// /// Represents whether a processor has matched/can handle processing the response. /// Values are of increasing priority. /// public enum MatchResult { /// /// No match, nothing to see here, move along /// NoMatch, /// /// Will accept anything /// DontCare, /// /// Matched, but in a non-specific way such as a wildcard match or fallback /// NonExactMatch, /// /// Exact specific match /// ExactMatch } } ================================================ FILE: src/Nancy/Responses/Negotiation/MediaRange.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Linq; /// /// Represents a media range from an accept header /// public class MediaRange : IEquatable { /// /// Initializes a new instance of the class, with /// the provided . /// /// string representation of a media range public MediaRange(string contentType) { this.ParseContentType(contentType); } private void ParseContentType(string contentType) { if (string.IsNullOrEmpty(contentType)) { this.Type = string.Empty; this.Subtype = string.Empty; this.Parameters = new MediaRangeParameters(); return; } if (contentType.Equals("*")) { contentType = "*/*"; } var parts = contentType.Split('/', ';'); if (parts.Length < 2) { { throw new ArgumentException("inputString not in correct Type/SubType format", contentType); } } this.Type = parts[0]; this.Subtype = parts[1].TrimEnd(); if (parts.Length <= 2) { this.Parameters = new MediaRangeParameters(); return; } var separator = contentType.IndexOf(';'); this.Parameters = MediaRangeParameters.FromString(contentType.Substring(separator)); } /// /// Media range type /// public MediaType Type { get; private set; } /// /// Media range subtype /// public MediaType Subtype { get; private set; } /// /// Media range parameters /// public MediaRangeParameters Parameters { get; private set; } /// /// Gets a value indicating if the media range is the */* wildcard /// public bool IsWildcard { get { return this.Type.IsWildcard && this.Subtype.IsWildcard; } } /// /// Whether or not a media range matches another, taking into account wildcards /// /// Other media range /// True if matching, false if not public bool Matches(MediaRange other) { return this.Type.Matches(other.Type) && this.Subtype.Matches(other.Subtype); } /// /// Whether or not a media range matches another, taking into account wildcards and parameters /// /// Other media range /// True if matching, false if not public bool MatchesWithParameters(MediaRange other) { return this.Matches(other) && this.Parameters.Matches(other.Parameters); } /// /// Performs an implicit conversion from to . /// /// Type of the content. /// /// The result of the conversion. /// public static implicit operator MediaRange(string contentType) { return new MediaRange(contentType); } /// /// Performs an implicit conversion from to . /// /// The media range. /// /// The result of the conversion. /// public static implicit operator string(MediaRange mediaRange) { if (null == mediaRange) { return null; } if (mediaRange.Parameters.Any()) { return string.Concat(mediaRange.Type, "/", mediaRange.Subtype, ";", mediaRange.Parameters); } return string.Concat(mediaRange.Type, "/", mediaRange.Subtype); } /// /// Indicates whether the current object is equal to another object of the same type. /// /// /// true if the current object is equal to the parameter; otherwise, false. /// /// An object to compare with this object. public bool Equals(MediaRange other) { return this.Matches(other); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/MediaRangeParameters.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections; using System.Collections.Generic; using System.Linq; /// /// Provides strongly-typed access to media range parameters. /// public class MediaRangeParameters : IEnumerable> { private readonly IDictionary parameters; /// /// Initializes a new instance of the class. /// public MediaRangeParameters() { this.parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// /// Initializes a new instance of the class, with /// the provided . /// /// The parameters. public MediaRangeParameters(IDictionary parameters) { this.parameters = new Dictionary(parameters, StringComparer.OrdinalIgnoreCase); } /// /// Gets the names of the available parameters. /// /// An containing the names of the parameters. public IEnumerable Keys { get { return this.parameters.Keys; } } /// /// Gets all the parameters values. /// /// An that contains all the parameters values. public IEnumerable Values { get { return this.parameters.Values; } } /// /// Returns an enumerator that iterates through the collection. /// /// A that can be used to iterate through the collection. public IEnumerator> GetEnumerator() { return this.parameters.GetEnumerator(); } /// /// Whether or not a set of media range parameters matches another, regardless of order /// /// Other media range parameters /// True if matching, false if not public bool Matches(MediaRangeParameters other) { return this.parameters.OrderBy(p => p.Key).SequenceEqual(other.parameters.OrderBy(p => p.Key)); } /// /// Returns an enumerator that iterates through a collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// /// Gets the value for the parameter identified by the parameter. /// /// The name of the parameter to return the value for. /// The value for the parameter. If the parameter is not defined then null is returned. public string this[string name] { get { string value; return this.parameters.TryGetValue(name, out value) ? value : null; } } /// /// Performs an implicit conversion from to . /// /// The media range parameters. /// /// The result of the conversion. /// public static implicit operator string(MediaRangeParameters mediaRangeParameters) { return string.Join(";", mediaRangeParameters.parameters.Select(p => p.Key + "=" + p.Value)); } /// /// Creates a MediaRangeParameters collection from a "a=1,b=2" string /// /// /// public static MediaRangeParameters FromString(string parameters) { var dictionary = parameters.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) .Select(part => part.Split('=')) .ToDictionary(split => split[0].Trim(), split => split[1].Trim()); return new MediaRangeParameters(dictionary); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/MediaType.cs ================================================ namespace Nancy.Responses.Negotiation { using System; /// /// Represents a media type or subtype in a . /// public class MediaType { private readonly string type; /// /// Initializes a new instance of the class, with /// the provided . /// /// the media type part public MediaType(string type) { this.type = type; } /// /// Gets a value indicating whether the media type is a wildcard or not /// /// if the media type is a wildcard, otherwise . public bool IsWildcard { get { return this.type != null && string.Equals(this.type, "*", StringComparison.Ordinal); } } /// /// Matched the media type with another media type. /// /// The media type that should be matched against. /// if the media types match, otherwise . public bool Matches(MediaType other) { return this.IsWildcard || other.IsWildcard || this.type.Equals(other.type, StringComparison.OrdinalIgnoreCase); } /// /// Performs an implicit conversion from to . /// /// The input string. /// /// The result of the conversion. /// public static implicit operator MediaType(string inputString) { return new MediaType(inputString); } /// /// Performs an implicit conversion from to . /// /// Type of the input media. /// /// The result of the conversion. /// public static implicit operator string(MediaType inputMediaType) { return inputMediaType.type; } /// /// Returns the type as a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this.type; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/NegotiationContext.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using System.Linq; using Nancy.Cookies; using Nancy.Extensions; /// /// Context for content negotiation. /// public class NegotiationContext { /// /// Initializes a new instance of the class. /// public NegotiationContext() { this.Cookies = new List(); this.PermissableMediaRanges = new List(new[] { (MediaRange)"*/*" }); this.MediaRangeModelMappings = new Dictionary>(); this.Headers = new Dictionary(); } /// /// Gets or sets additional cookies to assign to the response. /// /// An of instances. public IList Cookies { get; set; } /// /// Gets or sets the default model that will be used if a content type specific model is not specified. /// /// The default model instance. public dynamic DefaultModel { get; set; } /// /// Gets or sets the additional response headers required. /// /// An containing the headers. public IDictionary Headers { get; set; } /// /// Gets or sets the model mappings for media ranges. /// /// An containing the media range model mappings. public IDictionary> MediaRangeModelMappings { get; set; } /// /// The name of the that is locating a view. /// /// A containing the name of the module. public string ModuleName { get; set; } /// /// The module path of the that is locating a view. /// /// A containing the module path. public string ModulePath { get; set; } /// /// Gets or sets allowed media ranges. /// /// A list of the allowed media ranges. public IList PermissableMediaRanges { get; set; } /// /// Gets or sets the status code of the response. /// /// A value. public HttpStatusCode? StatusCode { get; set; } /// /// Gets or sets a text description of the HTTP status code returned to the client. /// /// The HTTP status code description. public string ReasonPhrase { get; set; } /// /// Gets or sets the view name if one is required. /// /// The name of the view that should be rendered. public string ViewName { get; set; } /// /// Gets the correct model for the given media range /// /// The to get the model for. /// The model for the provided if it has been mapped, otherwise the will be returned. public dynamic GetModelForMediaRange(MediaRange mediaRange) { var matching = this.MediaRangeModelMappings.Any(m => mediaRange.Matches(m.Key)); return matching ? this.MediaRangeModelMappings.First(m => mediaRange.Matches(m.Key)).Value.Invoke() : this.DefaultModel; } /// /// Sets the given Nancy module. /// /// The Nancy module instance. /// module public void SetModule(INancyModule module) { if (module == null) { throw new ArgumentNullException("module"); } this.ModuleName = module.GetModuleName(); this.ModulePath = module.ModulePath; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/Negotiator.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; /// /// Request response content negotiator. /// /// public class Negotiator : IHideObjectMembers { // TODO - this perhaps should be an interface, along with the view thing above // that would then wrap this to give more granular extension point for things like // AsNegotiated /// /// Initializes a new instance of the class, /// with the provided . /// /// The context that should be negotiated. public Negotiator(NancyContext context) { if (context == null) { throw new ArgumentNullException("context"); } this.NegotiationContext = context.NegotiationContext; } /// /// Gets the awaiter. /// /// public TaskAwaiter GetAwaiter() { return Task.FromResult(this).GetAwaiter(); } /// /// Gets the used by the negotiator. /// /// A instance. public NegotiationContext NegotiationContext { get; private set; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/ProcessorMatch.cs ================================================ namespace Nancy.Responses.Negotiation { /// /// Represents whether a processor has matched / can handle a requested response /// public class ProcessorMatch { /// /// A with both and set to . /// public static ProcessorMatch None = new ProcessorMatch { ModelResult = MatchResult.NoMatch, RequestedContentTypeResult = MatchResult.NoMatch }; /// /// Gets or sets the match result based on the content type /// public MatchResult RequestedContentTypeResult { get; set; } /// /// Gets or sets the match result based on the model /// public MatchResult ModelResult { get; set; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/ResponseProcessor.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using System.Linq; /// /// Processes negotiated responses of model type . /// public class ResponseProcessor : IResponseProcessor { /// /// Gets a set of mappings that map a given extension (such as .json) /// to a media range that can be sent to the client in a vary header. /// public IEnumerable> ExtensionMappings { get { return Enumerable.Empty>(); } } /// /// Determines whether the processor can handle a given content type and model. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A result that determines the priority of the processor. public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context) { return new ProcessorMatch { ModelResult = (model is Response) ? MatchResult.ExactMatch : MatchResult.NoMatch, RequestedContentTypeResult = MatchResult.DontCare }; } /// /// Process the response. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A instance. public Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context) { return (Response)model; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/ViewProcessor.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using Nancy.Configuration; using Nancy.ViewEngines; /// /// Processes the model for view requests. /// public class ViewProcessor : IResponseProcessor { private readonly IViewFactory viewFactory; private readonly TraceConfiguration traceConfiguration; /// /// Initializes a new instance of the class, /// with the provided . /// /// The view factory that should be used to render views. /// An instance. public ViewProcessor(IViewFactory viewFactory, INancyEnvironment environment) { this.viewFactory = viewFactory; this.traceConfiguration = environment.GetValue(); } /// /// Gets a set of mappings that map a given extension (such as .json) /// to a media range that can be sent to the client in a vary header. /// public IEnumerable> ExtensionMappings { get { yield break; } } /// /// Determines whether the processor can handle a given content type and model. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A result that determines the priority of the processor. public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context) { var matchingContentType = requestedMediaRange.Matches("text/html"); return matchingContentType ? new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.ExactMatch } : new ProcessorMatch(); } /// /// Process the response. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A instance. public Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context) { var viewResponse = this.viewFactory.RenderView(context.NegotiationContext.ViewName, model, GetViewLocationContext(context)); return this.traceConfiguration.DisplayErrorTraces ? new MaterialisingResponse(viewResponse) : viewResponse; } private static ViewLocationContext GetViewLocationContext(NancyContext context) { return new ViewLocationContext { Context = context, ModuleName = context.NegotiationContext.ModuleName, ModulePath = context.NegotiationContext.ModulePath }; } } } ================================================ FILE: src/Nancy/Responses/Negotiation/XmlProcessor.cs ================================================ namespace Nancy.Responses.Negotiation { using System; using System.Collections.Generic; using System.Linq; /// /// Processes the model for xml media types and extension. /// public class XmlProcessor : IResponseProcessor { private readonly ISerializer serializer; private static readonly IEnumerable> extensionMappings = new[] { new Tuple("xml", new MediaRange("application/xml")) }; /// /// Initializes a new instance of the class, /// with the provided . /// /// The serializes that the processor will use to process the request. public XmlProcessor(IEnumerable serializers) { this.serializer = serializers.FirstOrDefault(x => x.CanSerialize("application/xml")); } /// /// Gets a set of mappings that map a given extension (such as .json) /// to a media range that can be sent to the client in a vary header. /// public IEnumerable> ExtensionMappings { get { return extensionMappings; } } /// /// Determines whether the processor can handle a given content type and model. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A result that determines the priority of the processor. public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context) { if (IsExactXmlContentType(requestedMediaRange)) { return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.ExactMatch }; } if (IsWildcardXmlContentType(requestedMediaRange)) { return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.NonExactMatch }; } return new ProcessorMatch { ModelResult = MatchResult.DontCare, RequestedContentTypeResult = MatchResult.NoMatch }; } /// /// Process the response. /// /// Content type requested by the client. /// The model for the given media range. /// The nancy context. /// A instance. public Response Process(MediaRange requestedMediaRange, dynamic model, NancyContext context) { return CreateResponse(model, serializer); } private static Response CreateResponse(dynamic model, ISerializer serializer) { return new Response { Contents = stream => { if (model != null) { serializer.Serialize("application/xml", model, stream); } }, ContentType = "application/xml", StatusCode = HttpStatusCode.OK }; } private static bool IsExactXmlContentType(MediaRange requestedContentType) { if (requestedContentType.Type.IsWildcard && requestedContentType.Subtype.IsWildcard) { return true; } return requestedContentType.Matches("application/xml") || requestedContentType.Matches("text/xml"); } private static bool IsWildcardXmlContentType(MediaRange requestedContentType) { if (!requestedContentType.Type.IsWildcard && !string.Equals("application", requestedContentType.Type, StringComparison.OrdinalIgnoreCase)) { return false; } if (requestedContentType.Subtype.IsWildcard) { return true; } var subtypeString = requestedContentType.Subtype.ToString(); return subtypeString.EndsWith("+xml", StringComparison.OrdinalIgnoreCase); } } } ================================================ FILE: src/Nancy/Responses/NotAcceptableResponse.cs ================================================ namespace Nancy.Responses { /// /// Response with status code 406 (Not Acceptable). /// public class NotAcceptableResponse : Response { /// /// Initializes a new instance of the class. /// public NotAcceptableResponse() { this.StatusCode = HttpStatusCode.NotAcceptable; } } } ================================================ FILE: src/Nancy/Responses/RedirectResponse.cs ================================================ namespace Nancy.Responses { /// /// A response representing an HTTP redirect /// /// /// public class RedirectResponse : Response { /// /// Initializes a new instance of the class, with /// the provided and . /// /// Location to redirect to /// Type of redirection to perform public RedirectResponse(string location, RedirectType type = RedirectType.SeeOther) { this.Headers.Add("Location", location); this.Contents = GetStringContents(string.Empty); this.ContentType = "text/html"; switch (type) { case RedirectType.Permanent: this.StatusCode = HttpStatusCode.MovedPermanently; break; case RedirectType.Temporary: this.StatusCode = HttpStatusCode.TemporaryRedirect; break; default: this.StatusCode = HttpStatusCode.SeeOther; break; } } /// /// Which type of redirect /// public enum RedirectType { /// /// HTTP 301 - All future requests should be to this URL /// Permanent, /// /// HTTP 307 - Redirect this request but allow future requests to the original URL /// Temporary, /// /// HTTP 303 - Redirect this request using an HTTP GET /// SeeOther } } } ================================================ FILE: src/Nancy/Responses/StreamResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; /// /// Response that returns the contents of a stream of a given content-type. /// public class StreamResponse : Response { private Stream source; /// /// Initializes a new instance of the , with /// the provided and . /// /// The value producer for the response. /// The content-type of the stream contents. public StreamResponse(Func source, string contentType) { this.Contents = GetResponseBodyDelegate(source); this.ContentType = contentType; this.StatusCode = HttpStatusCode.OK; } private Action GetResponseBodyDelegate(Func sourceDelegate) { return stream => { using (this.source = sourceDelegate.Invoke()) { this.source.CopyTo(stream); } }; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public override void Dispose() { if (this.source != null) { this.source.Dispose(); } } } } ================================================ FILE: src/Nancy/Responses/TextResponse.cs ================================================ namespace Nancy.Responses { using System.Collections.Generic; using System.Text; using Nancy.Cookies; /// /// Represents a text (text/plain) response /// public class TextResponse : Response { private const string TextPlainContentType = "text/plain"; /// /// Creates a new instance of the TextResponse class, with /// the provided , and . /// /// Text content - defaults to empty if null /// Content Type - defaults to text/plain /// String encoding - UTF8 if null public TextResponse(string contents, string contentType = null, Encoding encoding = null) { if (encoding == null) { encoding = Encoding.UTF8; } if (string.IsNullOrEmpty(contentType)) { contentType = TextPlainContentType; } this.ContentType = GetContentType(contentType, encoding); this.StatusCode = HttpStatusCode.OK; if (contents != null) { this.Contents = stream => { var data = encoding.GetBytes(contents); stream.Write(data, 0, data.Length); }; } } /// /// Creates a new instance of the TextResponse class, with /// the provided , , , /// and . /// /// Status code - defaults to OK /// Text content - defaults to empty if null /// String encoding - UTF8 if null /// Headers if required /// Cookies if required public TextResponse(HttpStatusCode statusCode = HttpStatusCode.OK, string contents = null, Encoding encoding = null, IDictionary headers = null, IEnumerable cookies = null) { if (encoding == null) { encoding = Encoding.UTF8; } this.ContentType = GetContentType(TextPlainContentType, encoding); this.StatusCode = statusCode; if (contents != null) { this.Contents = stream => { var data = encoding.GetBytes(contents); stream.Write(data, 0, data.Length); }; } if (headers != null) { this.Headers = headers; } if (cookies != null) { foreach (var nancyCookie in cookies) { this.Cookies.Add(nancyCookie); } } } private static string GetContentType(string contentType, Encoding encoding) { return !contentType.Contains("charset") ? string.Concat(contentType, "; charset=", encoding.WebName) : contentType; } } } ================================================ FILE: src/Nancy/Responses/XmlResponse.cs ================================================ namespace Nancy.Responses { using System; using System.IO; using Nancy.Configuration; using Nancy.Xml; /// /// Represents an HTTP response with XML content. /// /// The type of the model. /// public class XmlResponse : Response { private readonly XmlConfiguration configuration; /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The model. /// The serializer. /// The environment. /// XML Serializer not set public XmlResponse(TModel model, ISerializer serializer, INancyEnvironment environment) { if (serializer == null) { throw new InvalidOperationException("XML Serializer not set"); } this.configuration = environment.GetValue(); this.Contents = GetXmlContents(model, serializer); this.ContentType = DefaultContentType; this.StatusCode = HttpStatusCode.OK; } private string DefaultContentType { get { return string.Concat("application/xml", this.Encoding); } } private string Encoding { get { return this.configuration.EncodingEnabled ? string.Concat("; charset=", this.configuration.DefaultEncoding.WebName) : string.Empty; } } private Action GetXmlContents(TModel model, ISerializer serializer) { return stream => serializer.Serialize(this.DefaultContentType, model, stream); } } } ================================================ FILE: src/Nancy/RouteConfiguration.cs ================================================ namespace Nancy { /// /// Configuration for the default routing. /// public class RouteConfiguration { /// /// A default instance of the class. /// public static readonly RouteConfiguration Default = new RouteConfiguration( disableMethodNotAllowedResponses: false, explicitHeadRouting: false); /// /// Initializes a new instance of the class. /// /// Determins is 405 responses are allowed. /// Enabled support for explicit HEAD route declarations. public RouteConfiguration(bool disableMethodNotAllowedResponses = false, bool explicitHeadRouting = false) { this.DisableMethodNotAllowedResponses = disableMethodNotAllowedResponses; this.ExplicitHeadRouting = explicitHeadRouting; } /// /// Gets a value indicating whether or not to respond with 405 responses. /// /// If 405 responses are allowed, otherwise . public bool DisableMethodNotAllowedResponses { get; private set; } /// /// Gets a value indicating whether or not to route HEAD requests explicitly. /// /// If explicit HEAD route requests are allowed, otherwise . public bool ExplicitHeadRouting { get; private set; } } } ================================================ FILE: src/Nancy/RouteConfigurationExtensions.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class RouteConfigurationExtensions { /// /// Configures . /// /// that should be configured. /// If 405 responses are allowed, otherwise . /// If explicit HEAD route requests are allowed, otherwise . public static void Routing(this INancyEnvironment environment, bool? disableMethodNotAllowedResponses = false, bool? explicitHeadRouting = false) { environment.AddValue(new RouteConfiguration( disableMethodNotAllowedResponses: disableMethodNotAllowedResponses ?? RouteConfiguration.Default.DisableMethodNotAllowedResponses, explicitHeadRouting: explicitHeadRouting ?? RouteConfiguration.Default.ExplicitHeadRouting)); } } } ================================================ FILE: src/Nancy/Routing/Constraints/AlphaRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System.Linq; /// /// Constraint for alphabetical route segments. /// public class AlphaRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "alpha"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out string matchedValue) { if (!segment.All(char.IsLetter)) { matchedValue = null; return false; } matchedValue = segment; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/BoolRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments. /// public class BoolRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "bool"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out bool matchedValue) { return bool.TryParse(segment, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/CustomDateTimeRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System; using System.Globalization; /// /// Constraint for route segments with custom format. /// public class CustomDateTimeRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "datetime"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out DateTime matchedValue) { return DateTime.TryParseExact(segment, parameters[0], CultureInfo.InvariantCulture, DateTimeStyles.None, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/DateTimeRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System; /// /// Constraint for route segments. /// public class DateTimeRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "datetime"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out DateTime matchedValue) { return DateTime.TryParse(segment, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/DecimalRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System.Globalization; /// /// Constraint for route segments. /// public class DecimalRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "decimal"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out decimal matchedValue) { return decimal.TryParse(segment, NumberStyles.Number, CultureInfo.InvariantCulture, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/GuidRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System; /// /// Constraint for route segments. /// public class GuidRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "guid"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out Guid matchedValue) { return Guid.TryParse(segment, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/IRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using Nancy.Routing.Trie; /// /// Defines the functionality to constrain route matching. /// public interface IRouteSegmentConstraint { /// /// Determines whether the given constraint should be matched. /// /// The route constraint. /// true if the constraint matches, false otherwise. bool Matches(string constraint); /// /// Matches the segment and parameter name against the constraint. /// /// The constraint. /// The segment. /// Name of the parameter. /// A containing information about the captured parameters. SegmentMatch GetMatch(string constraint, string segment, string parameterName); } } ================================================ FILE: src/Nancy/Routing/Constraints/IntRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System.Globalization; /// /// Constraint for route segments. /// public class IntRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "int"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out int matchedValue) { return int.TryParse(segment, NumberStyles.Integer, CultureInfo.InvariantCulture, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/LengthRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with a specific length. /// public class LengthRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "length"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out string matchedValue) { int minLength; int maxLength; if (parameters.Length == 2) { if (!this.TryParseInt(parameters[0], out minLength) || !this.TryParseInt(parameters[1], out maxLength)) { matchedValue = null; return false; } } else if (parameters.Length == 1) { minLength = 0; if (!this.TryParseInt(parameters[0], out maxLength)) { matchedValue = null; return false; } } else { matchedValue = null; return false; } if (segment.Length < minLength || segment.Length > maxLength) { matchedValue = null; return false; } matchedValue = segment; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/LongRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System.Globalization; /// /// Constraint for route segments. /// public class LongRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// /// The constraint's name. /// public override string Name { get { return "long"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out long matchedValue) { return long.TryParse(segment, NumberStyles.Integer, CultureInfo.InvariantCulture, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/Constraints/MaxLengthRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with a maximum length. /// public class MaxLengthRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// /// The constraint's name. /// public override string Name { get { return "maxlength"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out string matchedValue) { int maxLength; if (!this.TryParseInt(parameters[0], out maxLength)) { matchedValue = null; return false; } if (segment.Length > maxLength) { matchedValue = null; return false; } matchedValue = segment; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/MaxRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with a maximum value. /// public class MaxRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "max"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out int matchedValue) { int minValue; int intValue; if (!this.TryParseInt(parameters[0], out minValue) || !this.TryParseInt(segment, out intValue)) { matchedValue = default(int); return false; } if (intValue > minValue) { matchedValue = default(int); return false; } matchedValue = intValue; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/MinLengthRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with a minimum length. /// public class MinLengthRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "minlength"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out string matchedValue) { int minLength; if (!this.TryParseInt(parameters[0], out minLength)) { matchedValue = null; return false; } if (segment.Length < minLength) { matchedValue = null; return false; } matchedValue = segment; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/MinRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with a minimum length. /// public class MinRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// /// The constraint's name. /// public override string Name { get { return "min"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out int matchedValue) { int minValue; int intValue; if (!this.TryParseInt(parameters[0], out minValue) || !this.TryParseInt(segment, out intValue)) { matchedValue = default(int); return false; } if (intValue < minValue) { matchedValue = default(int); return false; } matchedValue = intValue; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/ParameterizedRouteSegmentConstraintBase.cs ================================================ namespace Nancy.Routing.Constraints { using System.Globalization; using System.Linq; /// /// Convenience class for implementing a route segment constraint that expects parameters. /// /// The type of parameter to capture. public abstract class ParameterizedRouteSegmentConstraintBase : RouteSegmentConstraintBase { /// /// Determines whether the given constraint matches the name of this constraint. /// /// The route constraint. /// /// if the segment matches the constraint, otherwise. /// public override bool Matches(string constraint) { return constraint.Contains('(') && constraint.Contains(')') && base.Matches(constraint.Substring(0, constraint.IndexOf('('))); } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out T matchedValue) { var parameters = constraint.Substring(constraint.IndexOf('(')).Trim('(', ')').Split(','); return TryMatch(segment, parameters, out matchedValue); } /// /// Tries to parse an integer using . /// /// The string value. /// The resulting integer. /// /// if the segment matches the constraint, otherwise. /// protected bool TryParseInt(string @string, out int result) { return int.TryParse(@string, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected abstract bool TryMatch(string segment, string[] parameters, out T matchedValue); } } ================================================ FILE: src/Nancy/Routing/Constraints/RangeRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { /// /// Constraint for route segments with value within a specified range. /// public class RangeRouteSegmentConstraint : ParameterizedRouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// /// The constraint's name. /// public override string Name { get { return "range"; } } /// /// Tries to match the given segment and parameters against the constraint. /// /// The segment to match. /// The parameters to match. /// The matched value. /// /// true if the segment and parameters matches the constraint, false otherwise. /// protected override bool TryMatch(string segment, string[] parameters, out int matchedValue) { int minRange; int maxRange; int intValue; if (parameters.Length == 2) { if (!this.TryParseInt(parameters[0], out minRange) || !this.TryParseInt(parameters[1], out maxRange) || !this.TryParseInt(segment, out intValue)) { matchedValue = default(int); return false; } } else { matchedValue = default(int); return false; } if (intValue < minRange || intValue > maxRange) { matchedValue = default(int); return false; } matchedValue = intValue; return true; } } } ================================================ FILE: src/Nancy/Routing/Constraints/RouteSegmentConstraintBase.cs ================================================ namespace Nancy.Routing.Constraints { using System; using Nancy.Routing.Trie; /// /// Convenience class for implementing a route segment constraint. /// /// The type of parameter to capture. public abstract class RouteSegmentConstraintBase : IRouteSegmentConstraint { /// /// Gets the name of the constraint. /// /// The constraint's name. public abstract string Name { get; } /// /// Determines whether the given constraint matches the name of this constraint. /// /// The route constraint. /// /// if the segment matches the constraint, otherwise. /// public virtual bool Matches(string constraint) { return constraint.Equals(Name, StringComparison.OrdinalIgnoreCase); } /// /// Matches the segment and parameter name against the constraint. /// /// The constraint. /// The segment. /// Name of the parameter. /// /// A containing information about the captured parameters /// stating whether there is a match or not. /// public SegmentMatch GetMatch(string constraint, string segment, string parameterName) { T value; if (this.TryMatch(constraint, segment, out value)) { return CreateMatch(parameterName, value); } return SegmentMatch.NoMatch; } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected abstract bool TryMatch(string constraint, string segment, out T matchedValue); private static SegmentMatch CreateMatch(string parameterName, object matchedValue) { var match = new SegmentMatch(true); match.CapturedParameters.Add(parameterName, matchedValue); return match; } } } ================================================ FILE: src/Nancy/Routing/Constraints/VersionRouteSegmentConstraint.cs ================================================ namespace Nancy.Routing.Constraints { using System; /// /// Constraint for version route segments. /// public class VersionRouteSegmentConstraint : RouteSegmentConstraintBase { /// /// Gets the name of the constraint. /// /// The constraint's name. public override string Name { get { return "version"; } } /// /// Tries to match the given segment against the constraint. /// /// The constraint. /// The segment to match. /// The matched value. /// /// if the segment matches the constraint, otherwise. /// protected override bool TryMatch(string constraint, string segment, out Version matchedValue) { return Version.TryParse(segment, out matchedValue); } } } ================================================ FILE: src/Nancy/Routing/DefaultNancyModuleBuilder.cs ================================================ namespace Nancy.Routing { using Nancy.Extensions; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Validation; using Nancy.ViewEngines; /// /// Default implementation for building a full configured instance. /// public class DefaultNancyModuleBuilder : INancyModuleBuilder { private readonly IViewFactory viewFactory; private readonly IResponseFormatterFactory responseFormatterFactory; private readonly IModelBinderLocator modelBinderLocator; private readonly IModelValidatorLocator validatorLocator; /// /// Initializes a new instance of the class. /// /// The instance that should be assigned to the module. /// An instance that should be used to create a response formatter for the module. /// A instance that should be assigned to the module. /// A instance that should be assigned to the module. public DefaultNancyModuleBuilder(IViewFactory viewFactory, IResponseFormatterFactory responseFormatterFactory, IModelBinderLocator modelBinderLocator, IModelValidatorLocator validatorLocator) { this.viewFactory = viewFactory; this.responseFormatterFactory = responseFormatterFactory; this.modelBinderLocator = modelBinderLocator; this.validatorLocator = validatorLocator; } /// /// Builds a fully configured instance, based upon the provided . /// /// The that should be configured. /// The current request context. /// A fully configured instance. public INancyModule BuildModule(INancyModule module, NancyContext context) { module.Context = context; module.Response = this.responseFormatterFactory.Create(context); module.ViewFactory = this.viewFactory; module.ModelBinderLocator = this.modelBinderLocator; module.ValidatorLocator = this.validatorLocator; return module; } } } ================================================ FILE: src/Nancy/Routing/DefaultRequestDispatcher.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Helpers; using Responses.Negotiation; /// /// Default implementation of a request dispatcher. /// public class DefaultRequestDispatcher : IRequestDispatcher { private readonly IRouteResolver routeResolver; private readonly IEnumerable responseProcessors; private readonly IRouteInvoker routeInvoker; private readonly IResponseNegotiator negotiator; /// /// Initializes a new instance of the class, with /// the provided , and . /// /// /// /// /// public DefaultRequestDispatcher(IRouteResolver routeResolver, IEnumerable responseProcessors, IRouteInvoker routeInvoker, IResponseNegotiator negotiator) { this.routeResolver = routeResolver; this.responseProcessors = responseProcessors; this.routeInvoker = routeInvoker; this.negotiator = negotiator; } /// /// Dispatches a requests. /// /// The for the current request. /// A cancellation token. public async Task Dispatch(NancyContext context, CancellationToken cancellationToken) { // TODO - May need to make this run off context rather than response .. seems a bit icky currently var resolveResult = this.Resolve(context); context.Parameters = resolveResult.Parameters; context.ResolvedRoute = resolveResult.Route; try { context.Response = await ExecuteRoutePreReq(context, cancellationToken, resolveResult.Before) .ConfigureAwait(false); if(context.Response == null) { context.Response = await this.routeInvoker.Invoke(resolveResult.Route, cancellationToken, resolveResult.Parameters, context) .ConfigureAwait(false); if (context.Request.Method.Equals("HEAD", StringComparison.OrdinalIgnoreCase)) { context.Response = new HeadResponse(context.Response); } } await this.ExecutePost(context, cancellationToken, resolveResult.After, resolveResult.OnError) .ConfigureAwait(false); } catch(Exception ex) { context.Response = this.ResolveErrorResult(context, resolveResult.OnError, ex); if (context.Response == null) { throw; } } return context.Response; } private async Task ExecutePost(NancyContext context, CancellationToken cancellationToken, AfterPipeline postHook, Func onError) { if (postHook == null) { return; } try { await postHook.Invoke(context, cancellationToken) .ConfigureAwait(false); } catch(Exception ex) { context.Response = this.ResolveErrorResult(context, onError, ex); if(context.Response == null) { throw; } } } private static Task ExecuteRoutePreReq(NancyContext context, CancellationToken cancellationToken, BeforePipeline resolveResultPreReq) { if (resolveResultPreReq == null) { return Task.FromResult(null); } return resolveResultPreReq.Invoke(context, cancellationToken); } private Response ResolveErrorResult(NancyContext context, Func resolveResultOnError, Exception exception) { if (resolveResultOnError != null) { var flattenedException = exception.FlattenInnerExceptions(); var result = resolveResultOnError.Invoke(context, flattenedException); if (result != null) { return this.negotiator.NegotiateResponse(result, context); } } return null; } private ResolveResult Resolve(NancyContext context) { var extension = context.Request.Path.IndexOfAny(Path.GetInvalidPathChars()) >= 0 ? null : Path.GetExtension(context.Request.Path); var originalAcceptHeaders = context.Request.Headers.Accept; var originalRequestPath = context.Request.Path; if (!string.IsNullOrEmpty(extension)) { var mappedMediaRanges = this.GetMediaRangesForExtension(extension.Substring(1)) .ToArray(); if (mappedMediaRanges.Any()) { var newMediaRanges = mappedMediaRanges.Where(x => !context.Request.Headers.Accept.Any(header => header.Equals(x))); var index = context.Request.Path.LastIndexOf(extension, StringComparison.Ordinal); var modifiedRequestPath = context.Request.Path.Remove (index, extension.Length); var match = this.InvokeRouteResolver(context, modifiedRequestPath, newMediaRanges); if (!(match.Route is NotFoundRoute)) { return match; } } } return this.InvokeRouteResolver(context, originalRequestPath, originalAcceptHeaders); } private IEnumerable> GetMediaRangesForExtension(string extension) { return this.responseProcessors .SelectMany(processor => processor.ExtensionMappings) .Where(mapping => mapping != null) .Where(mapping => mapping.Item1.Equals(extension, StringComparison.OrdinalIgnoreCase)) .Select(mapping => new Tuple(mapping.Item2, Decimal.MaxValue)) .Distinct(); } private ResolveResult InvokeRouteResolver(NancyContext context, string path, IEnumerable> acceptHeaders) { context.Request.Headers.Accept = acceptHeaders.ToList(); context.Request.Url.Path = path; return this.routeResolver.Resolve(context); } } } ================================================ FILE: src/Nancy/Routing/DefaultRouteCacheProvider.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.Linq; using Nancy.Diagnostics; /// /// It's not safe for a module to take a dependency on the cache (cyclic dependency) /// /// We provide an IRouteCacheProvider instead - the default implementation uses /// TinyIoC'd Func based lazy factory. /// public class DefaultRouteCacheProvider : IRouteCacheProvider, IDiagnosticsProvider { /// /// The route cache factory /// protected readonly Func RouteCacheFactory; /// /// Gets the name of the provider. /// /// A containing the name of the provider. public string Name { get { return "Route Cache"; } } /// /// Gets the description of the provider. /// /// A containing the description of the provider. public string Description { get { return "Provides methods for viewing and querying the route cache."; } } /// /// Gets the object that contains the interactive diagnostics methods. /// /// An instance of the interactive diagnostics object. public object DiagnosticObject { get; private set; } /// /// Initializes a new instance of the DefaultRouteCacheProvider class. /// /// public DefaultRouteCacheProvider(Func routeCacheFactory) { this.RouteCacheFactory = routeCacheFactory; this.DiagnosticObject = new RouteCacheDiagnostics(this); } /// /// Gets an instance of the route cache. /// /// An instance. public IRouteCache GetCache() { return this.RouteCacheFactory(); } private class RouteCacheDiagnostics { private readonly DefaultRouteCacheProvider cacheProvider; public RouteCacheDiagnostics(DefaultRouteCacheProvider cacheProvider) { this.cacheProvider = cacheProvider; } // ReSharper disable once UnusedMember.Local public IDictionary> GetAllRoutes() { var result = new Dictionary>(); foreach (var entry in this.cacheProvider.GetCache().Values.SelectMany(t => t.Select(t1 => t1.Item2))) { IList value; if (!result.TryGetValue(entry.Method, out value)) { value = new List(); result[entry.Method] = value; } value.Add(new { Name = entry.Name, Path = entry.Path }); } return result; } } } } ================================================ FILE: src/Nancy/Routing/DefaultRouteDescriptionProvider.cs ================================================ namespace Nancy.Routing { using System; using System.Linq; using System.Reflection; using System.Resources; /// /// Default implementation of the interface. Will look for /// route descriptions in resource files. The resource files should have the same name as the module /// for which it defines routes. /// public class DefaultRouteDescriptionProvider : IRouteDescriptionProvider { /// /// Get the description for a route. /// /// The module that the route is defined in. /// The path of the route that the description should be retrieved for. /// A containing the description of the route if it could be found, otherwise . public string GetDescription(INancyModule module, string path) { var assembly = module.GetType().GetTypeInfo().Assembly; if (assembly.IsDynamic) { return string.Empty; } var moduleName = string.Concat(module.GetType().FullName, ".resources"); var resourceName = assembly .GetManifestResourceNames() .FirstOrDefault(x => x.Equals(moduleName, StringComparison.OrdinalIgnoreCase)); if (resourceName != null) { var manager = new ResourceManager(resourceName.Replace(".resources", string.Empty), assembly); return manager.GetString(path); } return string.Empty; } } } ================================================ FILE: src/Nancy/Routing/DefaultRouteInvoker.cs ================================================ namespace Nancy.Routing { using System; using System.Threading; using System.Threading.Tasks; using Nancy.ErrorHandling; using Nancy.Extensions; using Nancy.Responses.Negotiation; /// /// Default route invoker implementation. /// public class DefaultRouteInvoker : IRouteInvoker { private readonly IResponseNegotiator negotiator; /// /// Initializes a new instance of the class. /// /// The response negotiator. public DefaultRouteInvoker(IResponseNegotiator negotiator) { this.negotiator = negotiator; } /// /// Invokes the specified with the provided . /// /// The route that should be invoked. /// Cancellation token /// The parameters that the route should be invoked with. /// The context of the route that is being invoked. /// A instance that represents the result of the invoked route. public async Task Invoke(Route route, CancellationToken cancellationToken, DynamicDictionary parameters, NancyContext context) { object result; try { result = await route.Invoke(parameters, cancellationToken).ConfigureAwait(false); } catch(RouteExecutionEarlyExitException earlyExitException) { context.WriteTraceLog( sb => sb.AppendFormat( "[DefaultRouteInvoker] Caught RouteExecutionEarlyExitException - reason {0}", earlyExitException.Reason)); return earlyExitException.Response; } if (!(result is ValueType) && result == null) { context.WriteTraceLog( sb => sb.AppendLine("[DefaultRouteInvoker] Invocation of route returned null")); result = new Response(); } return this.negotiator.NegotiateResponse(result, context); } } } ================================================ FILE: src/Nancy/Routing/DefaultRouteResolver.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.Linq; using Helpers; using Nancy.Configuration; using Trie; /// /// Default implementation of the interface. /// public class DefaultRouteResolver : IRouteResolver { private readonly INancyModuleCatalog catalog; private readonly INancyModuleBuilder moduleBuilder; private readonly IRouteCache routeCache; private readonly IRouteResolverTrie trie; private readonly Lazy configuration; private readonly GlobalizationConfiguration globalizationConfiguraton; /// /// Initializes a new instance of the class, using /// the provided , , /// and . /// /// An instance. /// An instance. /// An instance. /// An instance. /// An instance. public DefaultRouteResolver(INancyModuleCatalog catalog, INancyModuleBuilder moduleBuilder, IRouteCache routeCache, IRouteResolverTrie trie, INancyEnvironment environment) { this.catalog = catalog; this.moduleBuilder = moduleBuilder; this.routeCache = routeCache; this.trie = trie; this.configuration = new Lazy(environment.GetValue); this.globalizationConfiguraton = environment.GetValue(); this.BuildTrie(); } /// /// Gets the route, and the corresponding parameter dictionary from the URL /// /// Current context /// A containing the resolved route information. public ResolveResult Resolve(NancyContext context) { var pathDecoded = HttpUtility.UrlDecode(context.Request.Path); var results = this.trie.GetMatches(this.GetMethod(context), pathDecoded, context); if (!results.Any()) { var allowedMethods = this.trie.GetOptions(pathDecoded, context).ToArray(); if (IsOptionsRequest(context)) { return BuildOptionsResult(allowedMethods, context); } return this.IsMethodNotAllowed(allowedMethods) ? BuildMethodNotAllowedResult(context, allowedMethods) : GetNotFoundResult(context); } // Sort in descending order Array.Sort(results, (m1, m2) => -m1.CompareTo(m2)); for (var index = 0; index < results.Length; index++) { var matchResult = results[index]; if (matchResult.Condition == null || matchResult.Condition.Invoke(context)) { return this.BuildResult(context, matchResult); } } return GetNotFoundResult(context); } private static ResolveResult BuildMethodNotAllowedResult(NancyContext context, IEnumerable allowedMethods) { var route = new MethodNotAllowedRoute(context.Request.Path, context.Request.Method, allowedMethods); return new ResolveResult(route, new DynamicDictionary(), null, null, null); } private bool IsMethodNotAllowed(IEnumerable allowedMethods) { return allowedMethods.Any() && !this.configuration.Value.DisableMethodNotAllowedResponses; } private static bool IsOptionsRequest(NancyContext context) { return context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase); } private void BuildTrie() { this.trie.BuildTrie(this.routeCache); } private static ResolveResult BuildOptionsResult(IEnumerable allowedMethods, NancyContext context) { var path = context.Request.Path; var optionsResult = new OptionsRoute(path, allowedMethods); return new ResolveResult( optionsResult, new DynamicDictionary(), null, null, null); } private ResolveResult BuildResult(NancyContext context, MatchResult result) { var associatedModule = this.GetModuleFromMatchResult(context, result); context.NegotiationContext.SetModule(associatedModule); var route = associatedModule.Routes.ElementAt(result.RouteIndex); var parameters = DynamicDictionary.Create(result.Parameters, this.globalizationConfiguraton); return new ResolveResult { Route = route, Parameters = parameters, Before = associatedModule.Before, After = associatedModule.After, OnError = associatedModule.OnError }; } private INancyModule GetModuleFromMatchResult(NancyContext context, MatchResult result) { var module = this.catalog.GetModule(result.ModuleType, context); return this.moduleBuilder.BuildModule(module, context); } private static ResolveResult GetNotFoundResult(NancyContext context) { return new ResolveResult { Route = new NotFoundRoute(context.Request.Method, context.Request.Path), Parameters = DynamicDictionary.Empty, Before = null, After = null, OnError = null }; } private string GetMethod(NancyContext context) { var requestedMethod = context.Request.Method; if (!this.configuration.Value.ExplicitHeadRouting) { return requestedMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase) ? "GET" : requestedMethod; } return requestedMethod; } } } ================================================ FILE: src/Nancy/Routing/DefaultRouteSegmentExtractor.cs ================================================ namespace Nancy.Routing { using System.Collections.Generic; /// /// Default implementation of the interface. /// public class DefaultRouteSegmentExtractor : IRouteSegmentExtractor { /// /// Extracts the segments from the ; /// /// The path that the segments should be extracted from. /// An , containing the extracted segments. public IEnumerable Extract(string path) { var currentSegment = string.Empty; var openingParenthesesCount = 0; for (var index = 0; index < path.Length; index++) { var token = path[index]; if (token.Equals('(')) { openingParenthesesCount++; } if (token.Equals(')')) { openingParenthesesCount--; } if (!token.Equals('/') || openingParenthesesCount > 0) { currentSegment += token; } if ((token.Equals('/') || index == path.Length - 1) && currentSegment.Length > 0 && openingParenthesesCount == 0) { yield return currentSegment; currentSegment = string.Empty; } } } } } ================================================ FILE: src/Nancy/Routing/INancyModuleBuilder.cs ================================================ namespace Nancy.Routing { /// /// Defines the functionality to build a fully configured NancyModule instance. /// public interface INancyModuleBuilder { /// /// Builds a fully configured instance, based upon the provided . /// /// The that should be configured. /// The current request context. /// A fully configured instance. INancyModule BuildModule(INancyModule module, NancyContext context); } } ================================================ FILE: src/Nancy/Routing/IRequestDispatcher.cs ================================================ namespace Nancy.Routing { using System.Threading; using System.Threading.Tasks; /// /// Functionality for processing an incoming request. /// public interface IRequestDispatcher { /// /// Dispatches a requests. /// /// The for the current request. /// Cancellation token Task Dispatch(NancyContext context, CancellationToken cancellationToken); } } ================================================ FILE: src/Nancy/Routing/IRouteCache.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; /// /// Contains a cache of all routes registered in the system /// public interface IRouteCache : IDictionary>> { /// /// Gets a boolean value that indicates of the cache is empty or not. /// /// if the cache is empty, otherwise . bool IsEmpty(); } } ================================================ FILE: src/Nancy/Routing/IRouteCacheProvider.cs ================================================ namespace Nancy.Routing { /// /// It's not safe for a module to take a dependency on the cache (cyclic dependency) /// /// We provide an instead. /// /// It is *not* safe to call GetCache() inside a NancyModule constructor, although that shouldn't be necessary anyway. /// public interface IRouteCacheProvider { /// /// Gets an instance of the route cache. /// /// An instance. IRouteCache GetCache(); } } ================================================ FILE: src/Nancy/Routing/IRouteDescriptionProvider.cs ================================================ namespace Nancy.Routing { /// /// Defines the functionality for retrieving a description for a specific route. /// public interface IRouteDescriptionProvider { /// /// Get the description for a route. /// /// The module that the route is defined in. /// The path of the route that the description should be retrieved for. /// A containing the description of the route if it could be found, otherwise . string GetDescription(INancyModule module, string path); } } ================================================ FILE: src/Nancy/Routing/IRouteInvoker.cs ================================================ namespace Nancy.Routing { using System.Threading; using System.Threading.Tasks; /// /// Defines the functionality for invoking a and returning a /// public interface IRouteInvoker { /// /// Invokes the specified with the provided . /// /// The route that should be invoked. /// Cancellation token /// The parameters that the route should be invoked with. /// The context of the route that is being invoked. /// A instance that represents the result of the invoked route. Task Invoke(Route route, CancellationToken cancellationToken, DynamicDictionary parameters, NancyContext context); } } ================================================ FILE: src/Nancy/Routing/IRouteMetadataProvider.cs ================================================ namespace Nancy.Routing { using System; /// /// Defines the functionality for retrieving metadata for routes. /// public interface IRouteMetadataProvider { /// /// Gets the of the metadata that is created by the provider. /// /// The instance that the route is declared in. /// A for the route. /// A instance, or if nothing is found. Type GetMetadataType(INancyModule module, RouteDescription routeDescription); /// /// Gets the metadata for the provided route. /// /// The instance that the route is declared in. /// A for the route. /// An object representing the metadata for the given route, or if nothing is found. object GetMetadata(INancyModule module, RouteDescription routeDescription); } } ================================================ FILE: src/Nancy/Routing/IRouteResolver.cs ================================================ namespace Nancy.Routing { /// /// Returns a route that matches the request /// public interface IRouteResolver { /// /// Gets the route, and the corresponding parameter dictionary from the URL /// /// Current context /// A containing the resolved route information. ResolveResult Resolve(NancyContext context); } } ================================================ FILE: src/Nancy/Routing/IRouteSegmentExtractor.cs ================================================ namespace Nancy.Routing { using System.Collections.Generic; /// /// Defines the functionality for extracting the individual segments from a route path. /// public interface IRouteSegmentExtractor { /// /// Extracts the segments from the ; /// /// The path that the segments should be extracted from. /// An , containing the extracted segments. IEnumerable Extract(string path); } } ================================================ FILE: src/Nancy/Routing/MethodNotAllowedRoute.cs ================================================ namespace Nancy.Routing { using System.Collections.Generic; using System.Threading.Tasks; /// /// Route that is returned when the path could be matched but it was for the wrong request method. /// /// This is equal to sending back the 405 HTTP status code. public class MethodNotAllowedRoute : Route { /// /// Initializes a new instance of the type, for the /// specified , and . /// /// The path of the route. /// The HTTP method of the route. /// The HTTP methods that can be used to invoke the route. public MethodNotAllowedRoute(string path, string method, IEnumerable allowedMethods) : base(method, path, null, (x,c) => CreateMethodNotAllowedResponse(allowedMethods)) { } private static Task CreateMethodNotAllowedResponse(IEnumerable allowedMethods) { var response = new Response(); response.Headers["Allow"] = string.Join(", ", allowedMethods); response.StatusCode = HttpStatusCode.MethodNotAllowed; return Task.FromResult(response); } } } ================================================ FILE: src/Nancy/Routing/NotFoundRoute.cs ================================================ namespace Nancy.Routing { using System.Threading.Tasks; /// /// Route that is returned when the path could not be matched. /// /// This is equal to sending back the 404 HTTP status code. public class NotFoundRoute : Route { /// /// Initializes a new instance of the type, for the /// specified and . /// /// The HTTP method of the route. /// The path of the route. public NotFoundRoute(string method, string path) : base(method, path, null, (x,c) => Task.FromResult(new NotFoundResponse())) { } } } ================================================ FILE: src/Nancy/Routing/OptionsRoute.cs ================================================ namespace Nancy.Routing { using System.Collections.Generic; using System.Threading.Tasks; /// /// Route that is returned when the path could be matched but, the method was OPTIONS and there was no user defined handler for OPTIONS. /// public class OptionsRoute : Route { /// /// Initializes a new instance of the class, with /// the provided and . /// /// The request path. /// The list of allowed methods. public OptionsRoute(string path, IEnumerable allowedMethods) : base("OPTIONS", path, null, (x,c) => CreateMethodOptionsResponse(allowedMethods)) { } private static Task CreateMethodOptionsResponse(IEnumerable allowedMethods) { var response = new Response(); response.Headers["Allow"] = string.Join(", ", allowedMethods); response.StatusCode = HttpStatusCode.OK; return Task.FromResult(response); } } } ================================================ FILE: src/Nancy/Routing/ParameterSegmentInformation.cs ================================================ namespace Nancy.Routing { /// /// Information about a segment parameter. /// public class ParameterSegmentInformation { /// /// Initializes a new instance of the class. /// /// The name of the parameter /// The default value, if any, of the parameter. /// if the parameter is optional, otherwise . public ParameterSegmentInformation(string name, string defaultValue, bool isOptional) { this.Name = name; this.DefaultValue = defaultValue; this.IsOptional = isOptional; } /// /// Gets the default value for the parameter. /// public string DefaultValue { get; private set; } /// /// Gets the full name of the segment. /// /// Returns a string in one of the formats: {name}, {name?}, {name?defaultValue} depending on the kind of parameter. public string FullSegmentName { get { return (this.IsOptional) ? string.Concat(this.Name, "?", this.DefaultValue) : this.Name; } } /// /// Gets whether or not the parameter is optional. /// /// if the parameter is optional, otherwise . public bool IsOptional { get; private set; } /// /// Gets the name of the parameter. /// public string Name { get; private set; } } } ================================================ FILE: src/Nancy/Routing/ResolveResult.cs ================================================ namespace Nancy.Routing { using System; /// /// A class representing a route resolution result /// public class ResolveResult { /// /// Gets or sets the route /// public Route Route { get; set; } /// /// Gets or sets the captured parameters /// public DynamicDictionary Parameters { get; set; } /// /// Gets or sets the before module pipeline /// public BeforePipeline Before { get; set; } /// /// Gets or sets the after module pipeline /// public AfterPipeline After { get; set; } /// /// Gets or sets the on error module pipeline /// public Func OnError { get; set; } /// /// Initializes a new instance of the class. /// public ResolveResult() { } /// /// Initializes a new instance of the class, with /// the provided , , , /// and . /// /// The request route instance. /// The parameters. /// The before pipeline instance /// The after pipeline instace. /// The on error interceptor instance. public ResolveResult(Route route, DynamicDictionary parameters, BeforePipeline before, AfterPipeline after, Func onError) { this.Route = route; this.Parameters = parameters; this.Before = before; this.After = after; this.OnError = onError; } } } ================================================ FILE: src/Nancy/Routing/Route.cs ================================================ namespace Nancy.Routing { using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; /// /// Defines the core functionality of a route. /// public abstract class Route { /// /// Initializes a new instance of the type, with the specified . /// /// An instance. protected Route(RouteDescription description) { this.Description = description; } /// /// Initializes a new instance of the type, with the specified definition. /// /// Route name /// The HTTP method that the route is declared for. /// The path that the route is declared for. /// A condition that needs to be satisfied inorder for the route to be eligible for invocation. /// The of the value returned by the route. protected Route(string name, string method, string path, Func condition, Type returnType) : this(new RouteDescription(name, method, path, condition, returnType)) { } /// /// Gets the description of the route. /// /// A instance. public RouteDescription Description { get; private set; } /// /// Invokes the route with the provided . /// /// A that contains the parameters that should be passed to the route. /// Cancellation token /// The value that was produced by the route. public abstract Task Invoke(DynamicDictionary parameters, CancellationToken cancellationToken); } /// /// Stores information about a declared route in Nancy. /// [DebuggerDisplay("{Description.DebuggerDisplay, nq}")] public class Route : Route { /// /// Initializes a new instance of the type, with the specified . /// /// /// The action that should take place when the route is invoked. public Route(RouteDescription description, Func> action) : base(description) { this.Action = action; } /// /// Initializes a new instance of the type, with the specified definition. /// /// Route name /// The HTTP method that the route is declared for. /// The path that the route is declared for. /// A condition that needs to be satisfied inorder for the route to be eligible for invocation. /// The action that should take place when the route is invoked. public Route(string name, string method, string path, Func condition, Func> action) : this(new RouteDescription(name, method, path, condition, typeof(T)), action) { } /// /// Initializes a new instance of the type, with the specified definition. /// /// The HTTP method that the route is declared for. /// The path that the route is declared for. /// A condition that needs to be satisfied inorder for the route to be eligiable for invocation. /// The action that should take place when the route is invoked. public Route(string method, string path, Func condition, Func> action) : this(string.Empty, method, path, condition, action) { } /// /// Gets or sets the action that should take place when the route is invoked. /// /// A that represents the action of the route. public Func> Action { get; set; } /// /// Invokes the route with the provided . /// /// A that contains the parameters that should be passed to the route. /// Cancellation token /// A (hot) task of instance. public override async Task Invoke(DynamicDictionary parameters, CancellationToken cancellationToken) { return await this.Action.Invoke(parameters, cancellationToken).ConfigureAwait(false); } } } ================================================ FILE: src/Nancy/Routing/RouteCache.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.Linq; using Nancy.Culture; /// /// Caches information about all the available routes that was discovered by the bootstrapper. /// public class RouteCache : Dictionary>>, IRouteCache { private readonly IRouteSegmentExtractor routeSegmentExtractor; private readonly IRouteDescriptionProvider routeDescriptionProvider; private readonly IEnumerable routeMetadataProviders; /// /// Initializes a new instance of the class. /// /// The that should be used by the cache. /// The that should be used to create a context instance. /// /// /// /// public RouteCache( INancyModuleCatalog moduleCatalog, INancyContextFactory contextFactory, IRouteSegmentExtractor routeSegmentExtractor, IRouteDescriptionProvider routeDescriptionProvider, ICultureService cultureService, IEnumerable routeMetadataProviders) { this.routeSegmentExtractor = routeSegmentExtractor; this.routeDescriptionProvider = routeDescriptionProvider; this.routeMetadataProviders = routeMetadataProviders; var request = new Request("GET", "/", "http"); using (var context = contextFactory.Create(request)) { this.BuildCache(moduleCatalog.GetAllModules(context)); } } /// /// Gets a boolean value that indicates of the cache is empty or not. /// /// if the cache is empty, otherwise . public bool IsEmpty() { return !this.Values.SelectMany(r => r).Any(); } private void BuildCache(IEnumerable modules) { foreach (var module in modules) { var moduleType = module.GetType(); var routes = module.Routes.Select(r => r.Description).ToArray(); foreach (var routeDescription in routes) { routeDescription.Description = this.routeDescriptionProvider.GetDescription(module, routeDescription.Path); routeDescription.Segments = this.routeSegmentExtractor.Extract(routeDescription.Path).ToArray(); routeDescription.Metadata = this.GetRouteMetadata(module, routeDescription); } this.AddRoutesToCache(routes, moduleType); } } private RouteMetadata GetRouteMetadata(INancyModule module, RouteDescription routeDescription) { var data = new Dictionary(); foreach (var provider in this.routeMetadataProviders) { var type = provider.GetMetadataType(module, routeDescription); var metadata = provider.GetMetadata(module, routeDescription); if (type != null && metadata != null) { data.Add(type, metadata); } } return new RouteMetadata(data); } private void AddRoutesToCache(IEnumerable routes, Type moduleType) { List> routeDescriptions; if (!this.TryGetValue(moduleType, out routeDescriptions)) { routeDescriptions = new List>(); this[moduleType] = routeDescriptions; } routeDescriptions.AddRange(routes.Select((r, i) => new Tuple(i, r))); } } } ================================================ FILE: src/Nancy/Routing/RouteCacheExtensions.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.Linq; /// /// Contains extensions for the type. /// public static class RouteCacheExtensions { /// /// Retrieves metadata for all declared routes. /// /// The type of the metadata to retrieve. /// The to retrieve the metadata. /// An containing instances of the type. public static IEnumerable RetrieveMetadata(this IDictionary>> cache) { return cache.Values.SelectMany(x => x.Select(y => y.Item2.Metadata.Retrieve())); } } } ================================================ FILE: src/Nancy/Routing/RouteDescription.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; /// /// Represents the various parts of a route lambda. /// [DebuggerDisplay("{DebuggerDisplay, nq}")] public sealed class RouteDescription { /// /// Initializes a new instance of the class. /// /// Route name /// The request method of the route. /// The path that the route will be invoked for. /// The condition that has to be fulfilled for the route to be a valid match. /// The of the value returned by the route. public RouteDescription(string name, string method, string path, Func condition, Type returnType) { if (string.IsNullOrEmpty(method)) { throw new ArgumentException("Method must be specified", "method"); } if (string.IsNullOrEmpty(path)) { throw new ArgumentException("Path must be specified", "path"); } this.Name = name ?? string.Empty; this.Method = method; this.Path = path; this.Condition = condition; this.ReturnType = returnType; } /// /// The name of the route /// public string Name { get; set; } /// /// The condition that has to be fulfilled inorder for the route to be a valid match. /// /// A function that evaluates the condition when a instance is passed in. public Func Condition { get; private set; } /// /// The description of what the route is for. /// /// A containing the description of the route. public string Description { get; set; } /// /// Gets or sets the metadata information for a route. /// /// A instance. public RouteMetadata Metadata { get; set; } /// /// Gets the method of the route. /// /// A containing the method of the route. public string Method { get; private set; } /// /// Gets the path that the route will be invoked for. /// /// A containing the path of the route. public string Path { get; private set; } /// /// Gets or set the segments, for the route, that was returned by the . /// /// An , containing the segments for the route. public IEnumerable Segments { get; set; } /// /// Gets the of the value returned by the route. /// /// A instance. public Type ReturnType { get; private set; } private string DebuggerDisplay { get { var builder = new StringBuilder(); if (!string.IsNullOrEmpty(this.Name)) { builder.AppendFormat("{0} - ", this.Name); } builder.AppendFormat("{0} {1}", this.Method, this.Path); return builder.ToString(); } } } } ================================================ FILE: src/Nancy/Routing/RouteMetadata.cs ================================================ namespace Nancy.Routing { using System; using System.Collections.Generic; /// /// Stores metadata created by instances. /// public class RouteMetadata { /// /// Creates a new instance of the class. /// /// An containing the metadata, organised by the type that it is stored in. public RouteMetadata(IDictionary metadata) { this.Raw = metadata; } /// /// Gets the raw metadata . /// /// An instance. public IDictionary Raw { get; private set; } /// /// Gets a boolean that indicates if the specific type of metadata is stored. /// /// The type of the metadata to check for. /// if metadata, of the requested type is stored, otherwise . public bool Has() { return this.Raw.ContainsKey(typeof (TMetadata)); } /// /// Retrieves metadata of the provided type. /// /// The type of the metadata to retrieve. /// The metadata instance if available, otherwise . public TMetadata Retrieve() { var key = typeof(TMetadata); object value; return this.Raw.TryGetValue(key, out value) ? (TMetadata)value : default(TMetadata); } } } ================================================ FILE: src/Nancy/Routing/RouteMetadataProvider.cs ================================================ namespace Nancy.Routing { using System; /// /// Defines the functionality for retrieving metadata for routes. /// /// The metadata type. public abstract class RouteMetadataProvider : IRouteMetadataProvider { /// /// Gets the of the metadata that is created by the provider. /// /// The instance that the route is declared in. /// A for the route. /// A instance, or null if none are found. public Type GetMetadataType(INancyModule module, RouteDescription routeDescription) { return typeof(TMetadata); } /// /// Gets the metadata for the provided route. /// /// The instance that the route is declared in. /// A for the route. /// An instance of . public object GetMetadata(INancyModule module, RouteDescription routeDescription) { return this.GetRouteMetadata(module, routeDescription); } /// /// Gets the metadata for the provided route. /// /// The instance that the route is declared in. /// A for the route. /// An instance of . protected abstract TMetadata GetRouteMetadata(INancyModule module, RouteDescription routeDescription); } } ================================================ FILE: src/Nancy/Routing/Trie/IRouteResolverTrie.cs ================================================ namespace Nancy.Routing.Trie { using System.Collections.Generic; /// /// Trie structure for resolving routes /// public interface IRouteResolverTrie { /// /// Build the trie from the route cache /// /// The route cache void BuildTrie(IRouteCache cache); /// /// Get all matches for the given method and path /// /// HTTP method /// Requested path /// Current Nancy context /// An array of elements MatchResult[] GetMatches(string method, string path, NancyContext context); /// /// Get all method options for the given path /// /// Requested path /// Current Nancy context /// A collection of strings, each representing an allowed method IEnumerable GetOptions(string path, NancyContext context); } } ================================================ FILE: src/Nancy/Routing/Trie/ITrieNodeFactory.cs ================================================ namespace Nancy.Routing.Trie { using Nancy.Routing.Trie.Nodes; /// /// Factory for creating trie nodes from route definition segments /// public interface ITrieNodeFactory { /// /// Gets the correct Trie node type for the given segment /// /// Parent node /// Segment /// Corresponding TrieNode instance TrieNode GetNodeForSegment(TrieNode parent, string segment); } } ================================================ FILE: src/Nancy/Routing/Trie/MatchResult.cs ================================================ namespace Nancy.Routing.Trie { using System; using System.Collections.Generic; /// /// Match result for a matched route /// public class MatchResult : NodeData, IComparable { private static readonly MatchResult noMatch = new MatchResult(); private static readonly MatchResult[] noMatches = ArrayCache.Empty(); /// /// Gets or sets the captured parameters /// public IDictionary Parameters { get; set; } /// /// Gets the "no match" /// public static MatchResult NoMatch { get { return noMatch; } } /// /// Gets the "no matches" collection /// public static MatchResult[] NoMatches { get { return noMatches; } } /// /// Initializes a new instance of the class. /// /// The parameters. public MatchResult(IDictionary parameters) { this.Parameters = parameters; } /// /// Initializes a new instance of the class. /// public MatchResult() : this(new Dictionary()) { } /// /// Compares the current object with another object of the same type. /// /// /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the parameter.Zero This object is equal to . Greater than zero This object is greater than . /// /// An object to compare with this object. public int CompareTo(MatchResult other) { // Length of the route takes precedence over score if (this.RouteLength < other.RouteLength) { return -1; } if (this.RouteLength > other.RouteLength) { return 1; } if (this.Score < other.Score) { return -1; } if (this.Score > other.Score) { return 1; } if (Equals(this.ModuleType, other.ModuleType)) { if (this.RouteIndex < other.RouteIndex) { return -1; } if (this.RouteIndex > other.RouteIndex) { return 1; } } return 0; } } } ================================================ FILE: src/Nancy/Routing/Trie/NodeData.cs ================================================ namespace Nancy.Routing.Trie { using System; /// /// Represents a route that ends at a particular node. /// We store/calculate as much as we can at build time to save /// time during route matching. /// public class NodeData { /// /// Gets or sets the module type from the matching module /// public Type ModuleType { get; set; } /// /// Gets or sets the route method /// public string Method { get; set; } /// /// Gets or sets the index in the module routing table /// public int RouteIndex { get; set; } /// /// Gets or sets the number of segments in the route /// public int RouteLength { get; set; } /// /// Gets or sets the route score /// public int Score { get; set; } /// /// Gets or sets the route condition delegate /// public Func Condition { get; set; } } } ================================================ FILE: src/Nancy/Routing/Trie/NodeDataExtensions.cs ================================================ namespace Nancy.Routing.Trie { using System.Collections.Generic; /// /// Helpers methods for NodeData /// public static class NodeDataExtensions { /// /// Converts a instance into a /// /// Node data /// Captured parameters /// A instance public static MatchResult ToResult(this NodeData data, IDictionary parameters) { return new MatchResult(parameters) { ModuleType = data.ModuleType, Method = data.Method, RouteIndex = data.RouteIndex, RouteLength = data.RouteLength, Condition = data.Condition, Score = data.Score }; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/CaptureNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { /// /// A node for standard captures e.g. {foo} /// public class CaptureNode : TrieNode { private string parameterName; /// /// Score for this node /// public override int Score { get { return 1000; } } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public CaptureNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.ExtractParameterName(); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var match = new SegmentMatch(true); match.CapturedParameters.Add(this.parameterName, segment); return match; } private void ExtractParameterName() { this.parameterName = this.RouteDefinitionSegment.Trim('{', '}'); } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/CaptureNodeWithConstraint.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System.Collections.Generic; using System.Linq; using Nancy.Routing.Constraints; /// /// A node for constraint captures e.g. {foo:alpha}, {foo:datetime} /// public class CaptureNodeWithConstraint : TrieNode { private readonly IEnumerable routeSegmentConstraints; private string parameterName; private string constraint; /// /// Score for this node /// public override int Score { get { return 2000; } } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The parent. /// The segment. /// The node factory. /// The route segment constraints. public CaptureNodeWithConstraint(TrieNode parent, string segment, ITrieNodeFactory nodeFactory, IEnumerable routeSegmentConstraints) : base(parent, segment, nodeFactory) { this.routeSegmentConstraints = routeSegmentConstraints; this.ExtractParameterName(); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var routeSegmentConstraint = routeSegmentConstraints.FirstOrDefault(x => x.Matches(constraint)); if (routeSegmentConstraint == null) { return SegmentMatch.NoMatch; } return routeSegmentConstraint.GetMatch(this.constraint, segment, this.parameterName); } private void ExtractParameterName() { var segmentSplit = this.RouteDefinitionSegment.Trim('{', '}').Split(':'); this.parameterName = segmentSplit[0]; this.constraint = segmentSplit[1]; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/CaptureNodeWithDefaultValue.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; /// /// A capture node with a default value e.g. {foo?default} /// public class CaptureNodeWithDefaultValue : CaptureNode { private string parameterName; private string defaultValue; /// /// Score for this node /// public override int Score { get { return 1000; } } /// /// Initializes a new instance of the class, with /// the provided , and . /// /// The parent. /// The segment. /// The node factory. public CaptureNodeWithDefaultValue(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.ExtractParameterNameAndDefaultValue(); } /// /// Add a new route to the trie /// Adds itself as a normal capture node, but also sets a default capture /// on the parent and adds this node's children as children of the parent /// too (so it can effectively be "skipped" during matching) /// /// The segments of the route definition /// Current index in the segments array /// Current score for this route /// Number of nodes added for this route /// The module key the route comes from /// The route index in the module /// The route description public override void Add(string[] segments, int currentIndex, int currentScore, int nodeCount, Type moduleType, int routeIndex, RouteDescription routeDescription) { base.Add(segments, currentIndex, currentScore, nodeCount, moduleType, routeIndex, routeDescription); this.Parent.AdditionalParameters[this.parameterName] = this.defaultValue; // Keep the same index, reduce the node count and the score this.Parent.Add(segments, currentIndex, currentScore - this.Parent.Score, nodeCount - 1, moduleType, routeIndex, routeDescription); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var match = new SegmentMatch(true); match.CapturedParameters[this.parameterName] = segment; return match; } private void ExtractParameterNameAndDefaultValue() { var elements = this.RouteDefinitionSegment.Trim('{', '}').Split('?'); if (elements.Length != 2) { throw new InvalidOperationException(string.Format("Invalid capture route: {0}", this.RouteDefinitionSegment)); } this.parameterName = elements[0]; this.defaultValue = elements[1]; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/CaptureNodeWithMultipleParameters.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Nancy.Routing.Constraints; /// /// A node multiple standard captures combined with a literal e.g. {id}.png.{thing}.{otherthing} /// Captures parameters within segments that contain literals. /// i.e: /// /{file}.{name} /// /{file}.html /// /{major}.{minor}.{revision}B{build} /// public class CaptureNodeWithMultipleParameters : TrieNode { private static readonly Regex MatchRegex = new Regex(@"({?[^{}]*}?)", RegexOptions.Compiled); private readonly List parameterNames = new List(); private readonly List constraints = new List(); private string builtRegex = string.Empty; private IEnumerable routeSegmentConstraints; private const string AssertStart = "^"; private const string MatchParameter = "(.*)"; private const string AssertEnd = "$"; /// /// Initializes a new instance of the class, with /// the provided , , and . /// /// The parent node /// The segment to match upon /// The node factory. /// The route segment constraints. public CaptureNodeWithMultipleParameters(TrieNode parent, string segment, ITrieNodeFactory nodeFactory, IEnumerable routeSegmentConstraints) : base(parent, segment, nodeFactory) { this.routeSegmentConstraints = routeSegmentConstraints; this.ExtractParameterNames(); } /// /// Determines whether this TrieNode should be used for the given segment. /// /// The route segment /// a boolean public static bool IsMatch(string segment) { return MatchRegex.Matches(segment).Cast().Count(g => g.Value != string.Empty) > 1; } private static bool IsParameterCapture(Capture match) { return match.Value.StartsWith("{") && match.Value.EndsWith("}"); } /// /// Score for this node /// public override int Score { get { return 100; } } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var match = SegmentMatch.NoMatch; var regex = new Regex(this.builtRegex, StaticConfiguration.CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase); if (regex.IsMatch(segment)) { match = new SegmentMatch(true); var regexMatch = regex.Match(segment); for (var i = 1; i < regexMatch.Groups.Count; i++) { match.CapturedParameters.Add(this.parameterNames[i - 1], regexMatch.Groups[i].Value); if (!string.IsNullOrEmpty(this.constraints[i - 1])) { var routeSegmentConstraint = this.routeSegmentConstraints.FirstOrDefault(x => x.Matches(constraints[i-1])); if (routeSegmentConstraint == null || !routeSegmentConstraint.GetMatch(this.constraints[i - 1], regexMatch.Groups[i].Value, this.parameterNames[i - 1]).Matches) { return SegmentMatch.NoMatch; } } } } return match; } /// /// Extracts the parameter name and the literals for the segment /// private void ExtractParameterNames() { var matches = MatchRegex.Matches(this.RouteDefinitionSegment); this.BuildRegex(AssertStart); foreach (Match match in matches) { if (IsParameterCapture(match)) { if (match.Value.Contains(":")) { var segmentSplit = match.Value.Trim('{', '}').Split(':'); this.parameterNames.Add(segmentSplit[0]); this.constraints.Add(segmentSplit[1]); } else { this.parameterNames.Add(match.Value.Trim('{', '}')); this.constraints.Add(string.Empty); } this.BuildRegex(MatchParameter); } else { this.BuildRegex(Regex.Escape(match.Value)); } } this.BuildRegex(AssertEnd); } private void BuildRegex(string regexSegment) { this.builtRegex += regexSegment; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/GreedyCaptureNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; using System.Collections.Generic; using System.Linq; using System.Text; /// /// A greedy capture node e.g. {greedy*} /// e.g. /foo/bar/{greedy*} - this node will be hit for /foo/bar/[anything that doesn't match another route], but /// not for just /foo/bar /// e.g. /foo/{greedy*}/bar - this node will be hit for /foo/[anything that doesn't match another route]/bar /// public class GreedyCaptureNode : TrieNode { private string parameterName; /// /// Score for this node /// public override int Score { get { return 0; } } /// /// Initializes a new instance of the class with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public GreedyCaptureNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.GetParameterName(); } /// /// Gets all matches for a given requested route /// Overridden to handle greedy capturing /// /// Requested route segments /// Current index in the route segments /// Currently captured parameters /// Current Nancy context /// A collection of objects public override IEnumerable GetMatches(string[] segments, int currentIndex, IDictionary capturedParameters, NancyContext context) { var fullGreedy = this.GetFullGreedy(segments, currentIndex, capturedParameters); if (!this.Children.Any()) { return fullGreedy; } var sb = new StringBuilder(segments[currentIndex]); var results = new List(); currentIndex++; while (!this.NoMoreSegments(segments, currentIndex - 1)) { var currentSegment = segments[currentIndex]; TrieNode matchingChild; if (this.Children.TryGetValue(currentSegment, out matchingChild)) { var parameters = new Dictionary(capturedParameters); parameters[this.parameterName] = sb.ToString(); results.AddRange(matchingChild.GetMatches(segments, currentIndex, parameters, context)); } sb.AppendFormat("/{0}", currentSegment); currentIndex++; } if (!results.Any()) { results.AddRange(fullGreedy); } return results; } /// /// Matches the segment for a requested route /// Not-required or called for this node type /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { throw new NotSupportedException(); } private IEnumerable GetFullGreedy(string[] segments, int currentIndex, IDictionary capturedParameters) { if (!this.NodeData.Any()) { return ArrayCache.Empty(); } var value = segments.Skip(currentIndex).Aggregate((seg1, seg2) => seg1 + "/" + seg2); capturedParameters[this.parameterName] = value; return this.NodeData.Select(nd => nd.ToResult(capturedParameters)); } private void GetParameterName() { this.parameterName = this.RouteDefinitionSegment.Trim('{', '}').TrimEnd('*'); } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/GreedyRegExCaptureNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; /// /// A greedy regular expression capture node e.g. ^(?<id>\d{0,100})$ /// For use on an entire route path, regular expression must be surrounded by ^( )$ /// e.g. @"^(?:(?<id>videos/\d{1,10})(?:/{0,1}(?<slug>.*)))$" /// This will match for a Url like /videos/123/some-random-slug /// and capture 'videos/123' and 'some-random-slug' /// public class GreedyRegExCaptureNode : TrieNode { private Regex expression; private string[] groupNames; /// /// Score for this node /// public override int Score { get { return 100; } } /// /// Initializes a new instance of the class with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public GreedyRegExCaptureNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.BuildRegEx(); } /// /// Gets all matches for a given requested route /// Overridden to handle greedy capturing /// /// Requested route segments /// Current index in the route segments /// Currently captured parameters /// Current Nancy context /// A collection of objects public override IEnumerable GetMatches(string[] segments, int currentIndex, IDictionary capturedParameters, NancyContext context) { var value = segments.Skip(currentIndex).Aggregate((seg1, seg2) => seg1 + "/" + seg2); var match = this.expression.Match(value); if (!match.Success) { return ArrayCache.Empty(); } foreach (var groupName in this.groupNames) { var group = match.Groups[groupName]; if (group.Success) { capturedParameters.Add(groupName, group.Value); } } return this.NodeData.Select(nd => nd.ToResult(capturedParameters)); } /// /// Matches the segment for a requested route /// Not-required or called for this node type /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { throw new NotSupportedException(); } private void BuildRegEx() { this.expression = new Regex(this.RouteDefinitionSegment, RegexOptions.Compiled); this.groupNames = this.expression.GetGroupNames(); } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/LiteralNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; /// /// Literal string node e.g. goo /// public class LiteralNode : TrieNode { /// /// Score for this node /// public override int Score { get { return 10000; } } /// /// Initializes a new instance of the class with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public LiteralNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var comparisonType = StaticConfiguration.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; if (string.Equals( segment, this.RouteDefinitionSegment, comparisonType)) { return new SegmentMatch(true); } return SegmentMatch.NoMatch; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/OptionalCaptureNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; /// /// An optional capture node e.g. {foo?} /// public class OptionalCaptureNode : TrieNode { private string parameterName; /// /// Score for this node /// public override int Score { get { return 1000; } } /// /// Initializes a new instance of the class with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public OptionalCaptureNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.ExtractParameterName(); } /// /// Add a new route to the trie /// Adds itself as a normal capture node, but also adds this node's /// children as children of the parent too /// (so it can effectively be "skipped" during matching) /// /// The segments of the route definition /// Current index in the segments array /// Current score for this route /// Number of nodes added for this route /// The module key the route comes from /// The route index in the module /// The route description public override void Add(string[] segments, int currentIndex, int currentScore, int nodeCount, Type moduleType, int routeIndex, RouteDescription routeDescription) { base.Add(segments, currentIndex, currentScore, nodeCount, moduleType, routeIndex, routeDescription); // Keep the same index, reduce the node count and the score this.Parent.Add(segments, currentIndex, currentScore - this.Parent.Score, nodeCount - 1, moduleType, routeIndex, routeDescription); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var match = new SegmentMatch(true); match.CapturedParameters[this.parameterName] = segment; return match; } private void ExtractParameterName() { this.parameterName = this.RouteDefinitionSegment.Trim('{', '}').TrimEnd('?'); } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/RegExNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System.Text.RegularExpressions; /// /// A regular expression capture node e.g. (?<foo>\d{2,4}) /// public class RegExNode : TrieNode { private Regex expression; private string[] groupNames; /// /// Score for this node /// public override int Score { get { return 1000; } } /// /// Initializes a new instance of the class with /// the provided , and . /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes public RegExNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) : base(parent, segment, nodeFactory) { this.BuildRegEx(); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { var match = this.expression.Match(segment); if (!match.Success) { return SegmentMatch.NoMatch; } var result = new SegmentMatch(true); foreach (var groupName in this.groupNames) { var group = match.Groups[groupName]; if (group.Success) { result.CapturedParameters[groupName] = group.Value; } } return result; } private void BuildRegEx() { this.expression = new Regex(this.RouteDefinitionSegment, RegexOptions.Compiled); this.groupNames = this.expression.GetGroupNames(); } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/RootNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System.Collections.Generic; /// /// Root node of a trie /// public class RootNode : TrieNode { private SegmentMatch segmentMatch = new SegmentMatch(true); private readonly Dictionary localCaptures = new Dictionary(); /// /// Score for this node /// public override int Score { get { return 0; } } /// /// Initializes a new instance of the class class with /// the provided . /// /// The node factory. public RootNode(ITrieNodeFactory nodeFactory) : base(null, null, nodeFactory) { } /// /// Gets all matches for a given requested route /// /// Requested route segments /// Current index in the route segments /// Currently captured parameters /// Current Nancy context /// A collection of objects public override IEnumerable GetMatches(string[] segments, int currentIndex, IDictionary capturedParameters, NancyContext context) { if (segments.Length == 0) { return this.BuildResults(capturedParameters, this.localCaptures); } return this.GetMatchingChildren(segments, currentIndex, capturedParameters, this.localCaptures, context); } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public override SegmentMatch Match(string segment) { return this.segmentMatch; } } } ================================================ FILE: src/Nancy/Routing/Trie/Nodes/TrieNode.cs ================================================ namespace Nancy.Routing.Trie.Nodes { using System; using System.Collections.Generic; using System.Linq; /// /// A base class representing a node in the route trie /// public abstract class TrieNode { private readonly ITrieNodeFactory nodeFactory; /// /// Gets or sets the parent node /// public TrieNode Parent { get; protected set; } /// /// Gets or sets the segment from the route definition that this node represents /// public string RouteDefinitionSegment { get; protected set; } /// /// Gets or sets the children of this node /// public IDictionary Children { get; protected set; } /// /// Gets or sets the node data stored at this node, which will be converted /// into the if a match is found /// public IList NodeData { get; protected set; } /// /// Additional parameters to set that can be determined at trie build time /// public IDictionary AdditionalParameters { get; protected set; } /// /// Score for this node /// public abstract int Score { get; } /// /// Initializes a new instance of the class /// /// Parent node /// Segment of the route definition /// Factory for creating new nodes protected TrieNode(TrieNode parent, string segment, ITrieNodeFactory nodeFactory) { this.nodeFactory = nodeFactory; this.Parent = parent; this.RouteDefinitionSegment = segment; this.Children = new Dictionary(StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase); this.AdditionalParameters = new Dictionary(); this.NodeData = new List(); } /// /// Add a new route to the trie /// /// The segments of the route definition /// The module key the route comes from /// The route index in the module /// The route description public void Add(string[] segments, Type moduleType, int routeIndex, RouteDescription routeDescription) { this.Add(segments, -1, 0, 0, moduleType, routeIndex, routeDescription); } /// /// Add a new route to the trie /// /// The segments of the route definition /// Current index in the segments array /// Current score for this route /// Number of nodes added for this route /// The module key the route comes from /// The route index in the module /// The route description public virtual void Add(string[] segments, int currentIndex, int currentScore, int nodeCount, Type moduleType, int routeIndex, RouteDescription routeDescription) { if (this.NoMoreSegments(segments, currentIndex)) { this.NodeData.Add(this.BuildNodeData(nodeCount, currentScore + this.Score, moduleType, routeIndex, routeDescription)); return; } nodeCount++; currentIndex++; TrieNode child; if (!this.Children.TryGetValue(segments[currentIndex], out child)) { child = this.nodeFactory.GetNodeForSegment(this, segments[currentIndex]); this.Children.Add(segments[currentIndex], child); } child.Add(segments, currentIndex, currentScore + this.Score, nodeCount, moduleType, routeIndex, routeDescription); } /// /// Gets all matches for a given requested route /// /// Requested route segments /// Current Nancy context /// A collection of objects public virtual IEnumerable GetMatches(string[] segments, NancyContext context) { return this.GetMatches(segments, 0, new Dictionary(this.AdditionalParameters), context); } /// /// Gets all matches for a given requested route /// /// Requested route segments /// Current index in the route segments /// Currently captured parameters /// Current Nancy context /// A collection of objects public virtual IEnumerable GetMatches(string[] segments, int currentIndex, IDictionary capturedParameters, NancyContext context) { var segmentMatch = this.Match(segments[currentIndex]); if (segmentMatch == SegmentMatch.NoMatch) { return MatchResult.NoMatches; } if (this.NoMoreSegments(segments, currentIndex)) { return this.BuildResults(capturedParameters, segmentMatch.CapturedParameters) ?? MatchResult.NoMatches; } currentIndex++; return this.GetMatchingChildren(segments, currentIndex, capturedParameters, segmentMatch.CapturedParameters, context); } /// /// Gets a string representation of all routes /// /// Collection of strings, each representing a route public virtual IEnumerable GetRoutes() { var routeList = new List(this.Children.Values.SelectMany(c => c.GetRoutes()) .Select(s => (this.RouteDefinitionSegment ?? string.Empty) + "/" + s)); if (this.NodeData.Any()) { var node = this.NodeData.First(); var resultData = string.Format("{0} (Segments: {1} Score: {2})", this.RouteDefinitionSegment ?? "/", node.RouteLength, node.Score); routeList.Add(resultData); } return routeList; } /// /// Build the node data that will be used to create the /// We calculate/store as much as possible at build time to reduce match time. /// /// Number of nodes in the route /// Score for the route /// The module key the route comes from /// The route index in the module /// The route description /// A NodeData instance protected virtual NodeData BuildNodeData(int nodeCount, int score, Type moduleType, int routeIndex, RouteDescription routeDescription) { return new NodeData { Method = routeDescription.Method, RouteIndex = routeIndex, RouteLength = nodeCount, Score = score, Condition = routeDescription.Condition, ModuleType = moduleType, }; } /// /// Returns whether we are at the end of the segments /// /// Route segments /// Current index /// True if no more segments left, false otherwise protected bool NoMoreSegments(string[] segments, int currentIndex) { return currentIndex >= segments.Length - 1; } /// /// Build the results collection from the captured parameters if /// this node is the end result /// /// Currently captured parameters /// Parameters captured by the local matching /// Array of objects corresponding to each set of stored at this node protected IEnumerable BuildResults(IDictionary capturedParameters, IDictionary localCaptures) { if (!this.NodeData.Any()) { return MatchResult.NoMatches; } var parameters = new Dictionary(capturedParameters); if (this.AdditionalParameters.Any()) { foreach (var additionalParameter in this.AdditionalParameters) { if (!parameters.ContainsKey(additionalParameter.Key)) { parameters[additionalParameter.Key] = additionalParameter.Value; } } } if (localCaptures.Any()) { foreach (var localCapture in localCaptures) { parameters[localCapture.Key] = localCapture.Value; } } return this.NodeData.Select(n => n.ToResult(parameters)); } /// /// Gets all the matches from this node's children /// /// Requested route segments /// Current index /// Currently captured parameters /// Parameters captured by the local matching /// Current Nancy context /// Collection of objects protected IEnumerable GetMatchingChildren(string[] segments, int currentIndex, IDictionary capturedParameters, IDictionary localCaptures, NancyContext context) { var parameters = capturedParameters; if (localCaptures.Any() || this.AdditionalParameters.Any()) { parameters = new Dictionary(parameters); foreach (var localParameter in localCaptures) { parameters[localParameter.Key] = localParameter.Value; } foreach (var additionalParameter in this.AdditionalParameters) { parameters[additionalParameter.Key] = additionalParameter.Value; } } foreach (var childNode in this.Children.Values) { foreach (var match in childNode.GetMatches(segments, currentIndex, parameters, context)) { yield return match; } } } /// /// Matches the segment for a requested route /// /// Segment string /// A instance representing the result of the match public abstract SegmentMatch Match(string segment); } } ================================================ FILE: src/Nancy/Routing/Trie/RouteResolverTrie.cs ================================================ namespace Nancy.Routing.Trie { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Nodes; /// /// The default route resolution trie /// public class RouteResolverTrie : IRouteResolverTrie { private readonly ITrieNodeFactory nodeFactory; private readonly IDictionary routeTries = new Dictionary(StringComparer.OrdinalIgnoreCase); private static char[] splitSeparators = new[] {'/'}; /// /// Initializes a new instance of the class. /// /// The node factory. public RouteResolverTrie(ITrieNodeFactory nodeFactory) { this.nodeFactory = nodeFactory; } /// /// Build the trie from the route cache /// /// The route cache public void BuildTrie(IRouteCache cache) { foreach (var cacheItem in cache) { var moduleKey = cacheItem.Key; var routeDefinitions = cacheItem.Value; foreach (var routeDefinition in routeDefinitions) { var routeIndex = routeDefinition.Item1; var routeDescription = routeDefinition.Item2; TrieNode trieNode; if (!this.routeTries.TryGetValue(routeDescription.Method, out trieNode)) { trieNode = this.nodeFactory.GetNodeForSegment(null, null); this.routeTries.Add(routeDefinition.Item2.Method, trieNode); } var segments = routeDefinition.Item2.Segments.ToArray(); trieNode.Add(segments, moduleKey, routeIndex, routeDescription); } } } /// /// Get all matches for the given method and path /// /// HTTP method /// Requested path /// Current Nancy context /// An array of elements public MatchResult[] GetMatches(string method, string path, NancyContext context) { TrieNode result; if (!this.routeTries.TryGetValue(method, out result)) { return MatchResult.NoMatches; } return result.GetMatches(path.Split(splitSeparators, StringSplitOptions.RemoveEmptyEntries), context) .ToArray(); } /// /// Get all method options for the given path /// /// Requested path /// Current Nancy context /// A collection of strings, each representing an allowed method public IEnumerable GetOptions(string path, NancyContext context) { foreach (var method in this.routeTries.Keys) { if (this.GetMatches(method, path, context).Any()) { yield return method; } } } /// /// Returns a string that represents the current object. /// /// /// A string that represents the current object. /// /// 2 public override string ToString() { var sb = new StringBuilder(); foreach (var kvp in this.routeTries) { var method = kvp.Key; sb.Append( kvp.Value.GetRoutes().Select(s => method + " " + s) .Aggregate((r1, r2) => r1 + "\n" + r2)); } return sb.ToString(); } } } ================================================ FILE: src/Nancy/Routing/Trie/SegmentMatch.cs ================================================ namespace Nancy.Routing.Trie { using System.Collections.Generic; /// /// A segment match result /// public class SegmentMatch { private static SegmentMatch noMatch = new SegmentMatch(false); /// /// Gets a value indicating whether the match was successful or not /// public bool Matches { get; private set; } /// /// Gets a representing "no match" /// public static SegmentMatch NoMatch { get { return noMatch; } } /// /// Gets the captured parameters from the match, if the match was successful /// public IDictionary CapturedParameters { get; private set; } /// /// Initializes a new instance of the class, with /// the provided . /// /// if match was successful. public SegmentMatch(bool matches) { this.Matches = matches; if (matches) { this.CapturedParameters = new Dictionary(); } } } } ================================================ FILE: src/Nancy/Routing/Trie/TrieNodeFactory.cs ================================================ namespace Nancy.Routing.Trie { using System.Collections.Generic; using System.Linq; using Nancy.Routing.Constraints; using Nancy.Routing.Trie.Nodes; /// /// Factory for creating the correct type of TrieNode /// public class TrieNodeFactory : ITrieNodeFactory { private readonly IEnumerable routeSegmentConstraints; /// /// Initializes a new instance of the class with /// the provided . /// /// The route segment constraints. public TrieNodeFactory(IEnumerable routeSegmentConstraints) { this.routeSegmentConstraints = routeSegmentConstraints; } /// /// Gets the correct Trie node type for the given segment /// /// Parent node /// Segment /// TrieNode instance public virtual TrieNode GetNodeForSegment(TrieNode parent, string segment) { if (parent == null) { return new RootNode(this); } var chars = segment.ToCharArray(); var start = chars[0]; var end = chars[chars.Length - 1]; if (start == '(' && end == ')') { return new RegExNode(parent, segment, this); } if (start == '{' && end == '}' && chars.Count(c => c == '{' || c == '}') == 2) { return this.GetCaptureNode(parent, segment); } if (segment.StartsWith("^(") && (segment.EndsWith(")") || segment.EndsWith(")$"))) { return new GreedyRegExCaptureNode(parent, segment, this); } if (CaptureNodeWithMultipleParameters.IsMatch(segment)) { return new CaptureNodeWithMultipleParameters(parent, segment, this, routeSegmentConstraints); } return new LiteralNode(parent, segment, this); } private TrieNode GetCaptureNode(TrieNode parent, string segment) { if (segment.Contains(":")) { return new CaptureNodeWithConstraint(parent, segment, this, routeSegmentConstraints); } if (segment.EndsWith("?}")) { return new OptionalCaptureNode(parent, segment, this); } if (segment.EndsWith("*}")) { return new GreedyCaptureNode(parent, segment, this); } if (segment.Contains("?")) { return new CaptureNodeWithDefaultValue(parent, segment, this); } return new CaptureNode(parent, segment, this); } } } ================================================ FILE: src/Nancy/Security/ClaimsPrincipalExtensions.cs ================================================ namespace Nancy.Security { using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; /// /// Extension methods for working with IUserIdentity. /// public static class ClaimsPrincipalExtensions { /// /// Tests if the user is authenticated. /// /// User to be verified /// True if the user is authenticated, false otherwise public static bool IsAuthenticated(this ClaimsPrincipal user) { return user != null && user.Identity != null && user.Identity.IsAuthenticated; } /// /// Tests if the user has all of the required claims. /// /// User to be verified /// Claims the user needs to have /// True if the user has all of the required claims, false otherwise public static bool HasClaims(this ClaimsPrincipal user, params Predicate[] requiredClaims) { return user != null && requiredClaims.All(user.HasClaim); } /// /// Tests if the user has at least one of the required claims. /// /// User to be verified /// Claims the user needs to have at least one of /// True if the user has at least one of the required claims, false otherwise public static bool HasAnyClaim(this ClaimsPrincipal user, params Predicate[] requiredClaims) { return user != null && requiredClaims.Any(user.HasClaim); } /// /// Tests if the user has claims that satisfy the supplied validation function. /// /// User to be verified /// Validation function to be called with the authenticated /// users claims /// True if the user does pass the supplied validation function, false otherwise public static bool HasValidClaims(this ClaimsPrincipal user, Func, bool> isValid) { return user != null && user.Claims != null && isValid(user.Claims); } /// /// Tests if the user is in all of the required roles. /// /// User to be verified /// Roles the user needs to be in /// True if the user is in all of the required roles, false otherwise public static bool IsInRoles(this ClaimsPrincipal user, params string[] requiredRoles) { return user != null && requiredRoles != null && requiredRoles.All(user.IsInRole); } /// /// Tests if the user is in at least one of the required roles. /// /// User to be verified /// Roles the user needs to be in at least one of /// True if the user is in at least one of the required roles, false otherwise public static bool IsInAnyRole(this ClaimsPrincipal user, params string[] requiredRoles) { return user != null && requiredRoles != null && requiredRoles.Any(user.IsInRole); } } } ================================================ FILE: src/Nancy/Security/Csrf.cs ================================================ namespace Nancy.Security { using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using Nancy.Bootstrapper; using Nancy.Cookies; using Nancy.Cryptography; /// /// Csrf protection methods /// public static class Csrf { private const string CsrfHookName = "CsrfPostHook"; private const char ValueDelimiter = '#'; private const char PairDelimiter = '|'; /// /// Enables Csrf token generation. /// /// This is disabled by default. /// The application pipelines. /// The cryptography configuration. This is by default. /// Set the CSRF cookie secure flag. This is by default public static void Enable(IPipelines pipelines, CryptographyConfiguration cryptographyConfiguration = null, bool useSecureCookie = false) { cryptographyConfiguration = cryptographyConfiguration ?? CsrfApplicationStartup.CryptographyConfiguration; var postHook = new PipelineItem>( CsrfHookName, context => { if (context.Response == null || context.Response.Cookies == null || context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase)) { return; } object value; if (context.Items.TryGetValue(CsrfToken.DEFAULT_CSRF_KEY, out value)) { context.Response.Cookies.Add(new NancyCookie( CsrfToken.DEFAULT_CSRF_KEY, (string)value, true, useSecureCookie)); return; } string cookieValue; if (context.Request.Cookies.TryGetValue(CsrfToken.DEFAULT_CSRF_KEY, out cookieValue)) { var cookieToken = ParseToCsrfToken(cookieValue); if (CsrfApplicationStartup.TokenValidator.CookieTokenStillValid(cookieToken)) { context.Items[CsrfToken.DEFAULT_CSRF_KEY] = cookieValue; return; } } var tokenString = GenerateTokenString(cryptographyConfiguration); context.Items[CsrfToken.DEFAULT_CSRF_KEY] = tokenString; context.Response.Cookies.Add(new NancyCookie(CsrfToken.DEFAULT_CSRF_KEY, tokenString, true, useSecureCookie)); }); pipelines.AfterRequest.AddItemToEndOfPipeline(postHook); } /// /// Disable csrf token generation /// /// Application pipelines public static void Disable(IPipelines pipelines) { pipelines.AfterRequest.RemoveByName(CsrfHookName); } /// /// Creates a new csrf token for this response with an optional salt. /// Only necessary if a particular route requires a new token for each request. /// /// Nancy module /// The cryptography configuration. This is null by default. public static void CreateNewCsrfToken(this INancyModule module, CryptographyConfiguration cryptographyConfiguration = null) { var tokenString = GenerateTokenString(cryptographyConfiguration); module.Context.Items[CsrfToken.DEFAULT_CSRF_KEY] = tokenString; } /// /// Creates a new csrf token with an optional salt. /// Does not store the token in context. /// /// The generated token internal static string GenerateTokenString(CryptographyConfiguration cryptographyConfiguration = null) { cryptographyConfiguration = cryptographyConfiguration ?? CsrfApplicationStartup.CryptographyConfiguration; var token = new CsrfToken { CreatedDate = DateTimeOffset.Now }; token.CreateRandomBytes(); token.CreateHmac(cryptographyConfiguration.HmacProvider); var builder = new StringBuilder(); builder.AppendFormat("RandomBytes{0}{1}", ValueDelimiter, Convert.ToBase64String(token.RandomBytes)); builder.Append(PairDelimiter); builder.AppendFormat("Hmac{0}{1}", ValueDelimiter, Convert.ToBase64String(token.Hmac)); builder.Append(PairDelimiter); builder.AppendFormat("CreatedDate{0}{1}", ValueDelimiter, token.CreatedDate.ToString("o", CultureInfo.InvariantCulture)); return builder.ToString(); } /// /// Validate that the incoming request has valid CSRF tokens. /// Throws if validation fails. /// /// Module object /// Optional validity period before it times out /// If validation fails public static void ValidateCsrfToken(this INancyModule module, TimeSpan? validityPeriod = null) { var request = module.Request; if (request == null) { return; } var cookieToken = GetCookieToken(request); var providedToken = GetProvidedToken(request); var result = CsrfApplicationStartup.TokenValidator.Validate(cookieToken, providedToken, validityPeriod); if (result != CsrfTokenValidationResult.Ok) { throw new CsrfValidationException(result); } } private static CsrfToken GetProvidedToken(Request request) { CsrfToken providedToken = null; var providedTokenString = request.Form[CsrfToken.DEFAULT_CSRF_KEY].Value ?? request.Headers[CsrfToken.DEFAULT_CSRF_KEY].FirstOrDefault(); if (providedTokenString != null) { providedToken = ParseToCsrfToken(providedTokenString); } return providedToken; } private static CsrfToken GetCookieToken(Request request) { CsrfToken cookieToken = null; string cookieTokenString; if (request.Cookies.TryGetValue(CsrfToken.DEFAULT_CSRF_KEY, out cookieTokenString)) { cookieToken = ParseToCsrfToken(cookieTokenString); } return cookieToken; } private static void AddTokenValue(Dictionary dictionary, string key, string value) { if (!string.IsNullOrEmpty(key)) { dictionary.Add(key, value); } } private static CsrfToken ParseToCsrfToken(string cookieTokenString) { var parsed = new Dictionary(StringComparer.OrdinalIgnoreCase); var currentKey = string.Empty; var buffer = new StringBuilder(); for (var index = 0; index < cookieTokenString.Length; index++) { var currentCharacter = cookieTokenString[index]; switch (currentCharacter) { case ValueDelimiter: currentKey = buffer.ToString(); buffer.Clear(); break; case PairDelimiter: AddTokenValue(parsed, currentKey, buffer.ToString()); buffer.Clear(); break; default: buffer.Append(currentCharacter); break; } } AddTokenValue(parsed, currentKey, buffer.ToString()); if (parsed.Keys.Count() != 3) { return null; } try { return new CsrfToken { CreatedDate = DateTimeOffset.ParseExact(parsed["CreatedDate"], "o", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal), Hmac = Convert.FromBase64String(parsed["Hmac"]), RandomBytes = Convert.FromBase64String(parsed["RandomBytes"]) }; } catch { return null; } } } } ================================================ FILE: src/Nancy/Security/CsrfApplicationStartup.cs ================================================ namespace Nancy.Security { using Nancy.Bootstrapper; using Nancy.Cryptography; /// /// Wires up the CSRF (anti-forgery token) support at application startup. /// public class CsrfApplicationStartup : IApplicationStartup { /// /// Initializes a new instance of the class, using the /// provided and . /// /// The cryptographic configuration to use. /// The token validator that should be used. public CsrfApplicationStartup(CryptographyConfiguration cryptographyConfiguration, ICsrfTokenValidator tokenValidator) { CryptographyConfiguration = cryptographyConfiguration; TokenValidator = tokenValidator; } /// /// Gets the configured crypto config /// internal static CryptographyConfiguration CryptographyConfiguration { get; private set; } /// /// Gets the configured token validator /// internal static ICsrfTokenValidator TokenValidator { get; private set; } /// /// Perform any initialisation tasks /// /// Application pipelines public void Initialize(IPipelines pipelines) { } } } ================================================ FILE: src/Nancy/Security/CsrfToken.cs ================================================ namespace Nancy.Security { using System; using System.Linq; /// /// Represents a Csrf protection token /// #if !NETSTANDARD1_6 [Serializable] #endif public sealed class CsrfToken { /// /// The default key for the csrf cookie/form value/querystring value /// public const string DEFAULT_CSRF_KEY = "NCSRF"; /// /// Randomly generated bytes /// public byte[] RandomBytes { get; set; } /// /// Date and time the token was created /// public DateTimeOffset CreatedDate { get; set; } /// /// Tamper prevention hmac /// public byte[] Hmac { get; set; } /// /// Compares two instances. /// /// The to compare. /// /// if two instances are equal, otherwise. /// public bool Equals(CsrfToken other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return this.RandomBytes.SequenceEqual(other.RandomBytes) && other.CreatedDate.Equals(this.CreatedDate) && this.Hmac.SequenceEqual(other.Hmac); } /// /// Determines whether the specified is equal to the current . /// /// /// true if the specified is equal to the current ; otherwise, false. /// /// The to compare with the current . 2 public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != typeof(CsrfToken)) return false; return Equals((CsrfToken)obj); } /// /// Serves as a hash function for a particular type. /// /// /// A hash code for the current . /// /// 2 public override int GetHashCode() { unchecked { int result = (this.RandomBytes != null ? this.RandomBytes.GetHashCode() : 0); result = (result*397) ^ this.CreatedDate.GetHashCode(); result = (result*397) ^ (this.Hmac != null ? this.Hmac.GetHashCode() : 0); return result; } } /// /// Implements the operator == for instances. /// /// The left . /// The right . /// /// true if left and right instances are equal. /// public static bool operator ==(CsrfToken left, CsrfToken right) { return Equals(left, right); } /// /// Implements the operator != for instances. /// /// The left . /// The right . /// /// true if left and right instances are not equal. /// public static bool operator !=(CsrfToken left, CsrfToken right) { return !Equals(left, right); } } } ================================================ FILE: src/Nancy/Security/CsrfTokenExtensions.cs ================================================ namespace Nancy.Security { using System; using System.Linq; using System.Security.Cryptography; using Nancy.Cryptography; /// /// Extension methods for CSRF token related tasks. /// public static class CsrfTokenExtensions { private static readonly RandomNumberGenerator randomGenerator = RandomNumberGenerator.Create(); /// /// Gets a byte array representation of the csrf token for generating /// hmacs /// /// Token /// Byte array representing the token public static byte[] GetCsrfTokenBytes(this CsrfToken token) { return token.RandomBytes .Concat(BitConverter.GetBytes(token.CreatedDate.UtcTicks)) .ToArray(); } /// /// Calculates and sets the Hmac property on a given token /// /// Token /// Hmac provider to use /// Hmac bytes public static void CreateHmac(this CsrfToken token, IHmacProvider hmacProvider) { token.Hmac = hmacProvider.GenerateHmac(token.GetCsrfTokenBytes()); } /// /// Creates random bytes for the csrf token /// /// Random byte array public static void CreateRandomBytes(this CsrfToken token) { var randomBytes = new byte[10]; randomGenerator.GetBytes(randomBytes); token.RandomBytes = randomBytes; } } } ================================================ FILE: src/Nancy/Security/CsrfTokenValidationResult.cs ================================================ namespace Nancy.Security { /// /// Result of Csrf Token validation /// public enum CsrfTokenValidationResult { /// /// Validated ok /// Ok, /// /// One or both of the tokens appears to have been tampered with /// TokenTamperedWith, /// /// One or both of the tokens are missing /// TokenMissing, /// /// Tokens to not match /// TokenMismatch, /// /// Token is valid, but has expired /// TokenExpired, } } ================================================ FILE: src/Nancy/Security/CsrfValidationException.cs ================================================ namespace Nancy.Security { using System; /// /// Contains the exception information about a CSRF token validation. /// /// public class CsrfValidationException : Exception { /// /// Gets the result for the CSRF token validation. /// /// /// The result. /// public CsrfTokenValidationResult Result { get; private set; } /// /// Initializes a new instance of the class, with /// the provided . /// /// The CSRF token validation result. public CsrfValidationException(CsrfTokenValidationResult result) : base(result.ToString()) { Result = result; } } } ================================================ FILE: src/Nancy/Security/DefaultCsrfTokenValidator.cs ================================================ namespace Nancy.Security { using System; using System.Linq; using Nancy.Cryptography; /// /// The default implementation of the interface. /// public class DefaultCsrfTokenValidator : ICsrfTokenValidator { private readonly IHmacProvider hmacProvider; /// /// Initializes a new instance of the class, /// using the provided . /// /// The that should be used. public DefaultCsrfTokenValidator(CryptographyConfiguration cryptoConfig) { this.hmacProvider = cryptoConfig.HmacProvider; } /// /// Validates a pair of tokens /// /// First token (usually from either a form post or querystring) /// Second token (usually from a cookie) /// Optional period that the tokens are valid for /// Token validation result public CsrfTokenValidationResult Validate(CsrfToken tokenOne, CsrfToken tokenTwo, TimeSpan? validityPeriod = new TimeSpan?()) { if (tokenOne == null || tokenTwo == null) { return CsrfTokenValidationResult.TokenMissing; } if (!tokenOne.Equals(tokenTwo)) { return CsrfTokenValidationResult.TokenMismatch; } if (tokenOne.RandomBytes == null || tokenOne.RandomBytes.Length == 0) { return CsrfTokenValidationResult.TokenTamperedWith; } var newToken = new CsrfToken { CreatedDate = tokenOne.CreatedDate, RandomBytes = tokenOne.RandomBytes, }; newToken.CreateHmac(this.hmacProvider); if (!newToken.Hmac.SequenceEqual(tokenOne.Hmac)) { return CsrfTokenValidationResult.TokenTamperedWith; } if (validityPeriod.HasValue) { var expiryDate = tokenOne.CreatedDate.Add(validityPeriod.Value); if (DateTimeOffset.Now > expiryDate) { return CsrfTokenValidationResult.TokenExpired; } } return CsrfTokenValidationResult.Ok; } /// /// Validates that a cookie token is still valid with the current configuration / keys /// /// Token to validate /// True if valid, false otherwise public bool CookieTokenStillValid(CsrfToken cookieToken) { if (cookieToken == null || cookieToken.RandomBytes == null || cookieToken.RandomBytes.Length == 0) { return false; } var newToken = new CsrfToken { CreatedDate = cookieToken.CreatedDate, RandomBytes = cookieToken.RandomBytes, }; newToken.CreateHmac(this.hmacProvider); if (!newToken.Hmac.SequenceEqual(cookieToken.Hmac)) { return false; } return true; } } } ================================================ FILE: src/Nancy/Security/ICsrfTokenValidator.cs ================================================ namespace Nancy.Security { using System; /// /// Validates Csrf tokens /// public interface ICsrfTokenValidator { /// /// Validates a pair of tokens /// /// First token (usually from either a form post or querystring) /// Second token (usually from a cookie) /// Optional period that the tokens are valid for /// Token validation result CsrfTokenValidationResult Validate(CsrfToken tokenOne, CsrfToken tokenTwo, TimeSpan? validityPeriod = null); /// /// Validates that a cookie token is still valid with the current configuration / keys /// /// Token to validate /// True if valid, false otherwise bool CookieTokenStillValid(CsrfToken cookieToken); } } ================================================ FILE: src/Nancy/Security/ModuleSecurity.cs ================================================ namespace Nancy.Security { using System; using System.Linq; using System.Security.Claims; using Nancy.Extensions; using Nancy.Responses; /// /// Some simple helpers give some nice authentication syntax in the modules. /// public static class ModuleSecurity { /// /// This module requires authentication /// /// Module to enable public static void RequiresAuthentication(this INancyModule module) { module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication"); } /// /// This module requires authentication and certain claims to be present. /// /// Module to enable /// Claim(s) required public static void RequiresClaims(this INancyModule module, params Predicate[] requiredClaims) { module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication"); module.AddBeforeHookOrExecute(SecurityHooks.RequiresClaims(requiredClaims), "Requires Claims"); } /// /// This module requires authentication and any one of certain claims to be present. /// /// Module to enable /// Claim(s) required public static void RequiresAnyClaim(this INancyModule module, params Predicate[] requiredClaims) { module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication"); module.AddBeforeHookOrExecute(SecurityHooks.RequiresAnyClaim(requiredClaims), "Requires Any Claim"); } /// /// This module requires authentication and certain roles to be present. /// /// Module to enable /// Role(s) required public static void RequiresRoles(this INancyModule module, params string[] requiredRoles) { module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication"); module.AddBeforeHookOrExecute(SecurityHooks.RequiresRoles(requiredRoles), "Requires Roles"); } /// /// This module requires authentication and any one of certain roles to be present. /// /// Module to enable /// Role(s) at least one of which is required public static void RequiresAnyRole(this INancyModule module, params string[] requiredRoles) { module.AddBeforeHookOrExecute(SecurityHooks.RequiresAuthentication(), "Requires Authentication"); module.AddBeforeHookOrExecute(SecurityHooks.RequiresAnyRole(requiredRoles), "Requires Any Role"); } /// /// This module requires https. /// /// The that requires HTTPS. public static void RequiresHttps(this INancyModule module) { module.RequiresHttps(true); } /// /// This module requires https. /// /// The that requires HTTPS. /// if the user should be redirected to HTTPS (no port number) if the incoming request was made using HTTP, otherwise if should be returned. public static void RequiresHttps(this INancyModule module, bool redirect) { module.Before.AddItemToEndOfPipeline(SecurityHooks.RequiresHttps(redirect, null)); } /// /// This module requires https. /// /// The that requires HTTPS. /// if the user should be redirected to HTTPS if the incoming request was made using HTTP, otherwise if should be returned. /// The HTTPS port number to use public static void RequiresHttps(this INancyModule module, bool redirect, int httpsPort) { module.Before.AddItemToEndOfPipeline(SecurityHooks.RequiresHttps(redirect, httpsPort)); } } } ================================================ FILE: src/Nancy/Security/SSLProxy.cs ================================================ namespace Nancy.Security { using System; using System.Linq; using Nancy.Bootstrapper; /// /// Allows a BeforeRequest hook to change Url to HTTPS if X-Forwarded-Proto header present /// public static class SSLProxy { /// /// Checks for Forwarded or X-Forwarded-Proto header and if so makes current url scheme https /// /// Application pipelines public static void RewriteSchemeUsingForwardedHeaders(IPipelines pipelines) { pipelines.BeforeRequest += ctx => { //X-Forwarded-Proto: https if (ctx.Request.Headers.Keys.Any(x => x.Equals("X-Forwarded-Proto", StringComparison.OrdinalIgnoreCase))) { if (ctx.Request.Headers["X-Forwarded-Proto"].Contains("https", StringComparer.OrdinalIgnoreCase)) { ctx.Request.Url.Scheme = "https"; } } //RFC7239 if (ctx.Request.Headers.Keys.Any(x => x.Equals("Forwarded", StringComparison.OrdinalIgnoreCase))) { var forwardedHeader = ctx.Request.Headers["Forwarded"]; var protoValue = forwardedHeader.FirstOrDefault(x => x.StartsWith("proto", StringComparison.OrdinalIgnoreCase)); if (protoValue != null && protoValue.Equals("proto=https", StringComparison.OrdinalIgnoreCase)) { ctx.Request.Url.Scheme = "https"; } } return null; }; } } } ================================================ FILE: src/Nancy/Security/SecurityHooks.cs ================================================ namespace Nancy.Security { using System; using System.Collections.Generic; using System.Security.Claims; using Nancy.Responses; /// /// Hooks to be used in a request pipeline. /// public static class SecurityHooks { /// /// Creates a hook to be used in a pipeline before a route handler to ensure that /// the request was made by an authenticated user. /// /// Hook that returns an Unauthorized response if not authenticated in, /// null otherwise public static Func RequiresAuthentication() { return UnauthorizedIfNot(ctx => ctx.CurrentUser.IsAuthenticated()); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure /// that the request was made by an authenticated user having the required claims. /// /// Claims the authenticated user needs to have /// Hook that returns an Unauthorized response if the user is not /// authenticated or does not have the required claims, null otherwise public static Func RequiresClaims(params Predicate[] claims) { return ForbiddenIfNot(ctx => ctx.CurrentUser.HasClaims(claims)); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure /// that the request was made by an authenticated user having at least one of /// the required claims. /// /// Claims the authenticated user needs to have at least one of /// Hook that returns an Unauthorized response if the user is not /// authenticated or does not have at least one of the required claims, null /// otherwise public static Func RequiresAnyClaim(params Predicate[] claims) { return ForbiddenIfNot(ctx => ctx.CurrentUser.HasAnyClaim(claims)); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure /// that the request was made by an authenticated user whose claims satisfy the /// supplied validation function. /// /// Validation function to be called with the authenticated /// users claims /// Hook that returns an Unauthorized response if the user is not /// authenticated or does not pass the supplied validation function, null /// otherwise public static Func RequiresValidatedClaims(Func, bool> isValid) { return ForbiddenIfNot(ctx => ctx.CurrentUser.HasValidClaims(isValid)); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure /// that the request was made by an authenticated user being in all of /// the required roles. /// /// Roles the authenticated user needs to be in /// Hook that returns an Unauthorized response if the user is not /// authenticated or is not in all of the required roles, null /// otherwise public static Func RequiresRoles(params string[] roles) { return ForbiddenIfNot(ctx => ctx.CurrentUser.IsInRoles(roles)); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure /// that the request was made by an authenticated user being in at least one of /// the required roles. /// /// Roles the authenticated user needs to be in at least one of /// Hook that returns an Unauthorized response if the user is not /// authenticated or is not in at least one of the required roles, null /// otherwise public static Func RequiresAnyRole(params string[] roles) { return ForbiddenIfNot(ctx => ctx.CurrentUser.IsInAnyRole(roles)); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure that /// the request satisfies a specific test. /// /// Test that must return true for the request to continue /// Hook that returns an Unauthorized response if the test fails, null otherwise private static Func UnauthorizedIfNot(Func test) { return HttpStatusCodeIfNot(HttpStatusCode.Unauthorized, test); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure that /// the request satisfies a specific test. /// /// Test that must return true for the request to continue /// Hook that returns an Forbidden response if the test fails, null otherwise private static Func ForbiddenIfNot(Func test) { return HttpStatusCodeIfNot(HttpStatusCode.Forbidden, test); } /// /// Creates a hook to be used in a pipeline before a route handler to ensure that /// the request satisfies a specific test. /// /// HttpStatusCode to use for the response /// Test that must return true for the request to continue /// Hook that returns a response with a specific HttpStatusCode if the test fails, null otherwise private static Func HttpStatusCodeIfNot(HttpStatusCode statusCode, Func test) { return (ctx) => { Response response = null; if (!test(ctx)) { response = new Response { StatusCode = statusCode }; } return response; }; } /// /// Creates a hook to be used in a pipeline before a route handler to ensure that /// the resource is served over HTTPS /// /// if the user should be redirected to HTTPS (no port number) if the incoming request was made using HTTP, otherwise if should be returned. /// The HTTPS port number to use /// Hook that returns a with the Url scheme set to HTTPS, /// or a with a status code if redirect is false or the method is not GET, /// null otherwise public static Func RequiresHttps(bool redirect, int? httpsPort = null) { return (ctx) => { Response response = null; var request = ctx.Request; if (!request.Url.IsSecure) { if (redirect && request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase)) { var redirectUrl = request.Url.Clone(); redirectUrl.Port = httpsPort; redirectUrl.Scheme = "https"; response = new RedirectResponse(redirectUrl.ToString()); } else { response = new Response { StatusCode = HttpStatusCode.Forbidden }; } } return response; }; } } } ================================================ FILE: src/Nancy/Session/CookieBasedSessions.cs ================================================ namespace Nancy.Session { using System; using System.Collections.Generic; using System.Linq; using System.Text; using Nancy.Bootstrapper; using Nancy.Cookies; using Nancy.Cryptography; using Nancy.Helpers; /// /// Cookie based session storage /// public class CookieBasedSessions : IObjectSerializerSelector { private readonly CookieBasedSessionsConfiguration currentConfiguration; /// /// Gets the cookie name that the session is stored in /// /// Cookie name public string CookieName { get { return this.currentConfiguration.CookieName; } } /// /// Initializes a new instance of the class. /// /// The encryption provider. /// The hmac provider /// Session object serializer to use public CookieBasedSessions(IEncryptionProvider encryptionProvider, IHmacProvider hmacProvider, IObjectSerializer objectSerializer) { this.currentConfiguration = new CookieBasedSessionsConfiguration { Serializer = objectSerializer, CryptographyConfiguration = new CryptographyConfiguration(encryptionProvider, hmacProvider) }; } /// /// Initializes a new instance of the class. /// /// Cookie based sessions configuration. public CookieBasedSessions(CookieBasedSessionsConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } if (!configuration.IsValid) { throw new ArgumentException("Configuration is invalid", "configuration"); } this.currentConfiguration = configuration; } /// /// Initialise and add cookie based session hooks to the application pipeline /// /// Application pipelines /// Cookie based sessions configuration. /// Formatter selector for choosing a non-default serializer public static IObjectSerializerSelector Enable(IPipelines pipelines, CookieBasedSessionsConfiguration configuration) { if (pipelines == null) { throw new ArgumentNullException("pipelines"); } var sessionStore = new CookieBasedSessions(configuration); pipelines.BeforeRequest.AddItemToStartOfPipeline(ctx => LoadSession(ctx, sessionStore)); pipelines.AfterRequest.AddItemToEndOfPipeline(ctx => SaveSession(ctx, sessionStore)); return sessionStore; } /// /// Initialise and add cookie based session hooks to the application pipeline /// /// Application pipelines /// Cryptography configuration /// Formatter selector for choosing a non-default serializer public static IObjectSerializerSelector Enable(IPipelines pipelines, CryptographyConfiguration cryptographyConfiguration) { var cookieBasedSessionsConfiguration = new CookieBasedSessionsConfiguration(cryptographyConfiguration) { Serializer = new DefaultObjectSerializer() }; return Enable(pipelines, cookieBasedSessionsConfiguration); } /// /// Initialise and add cookie based session hooks to the application pipeline with the default encryption provider. /// /// Application pipelines /// Formatter selector for choosing a non-default serializer public static IObjectSerializerSelector Enable(IPipelines pipelines) { return Enable(pipelines, new CookieBasedSessionsConfiguration { Serializer = new DefaultObjectSerializer() }); } /// /// Using the specified serializer /// /// Formatter to use public void WithSerializer(IObjectSerializer newSerializer) { this.currentConfiguration.Serializer = newSerializer; } /// /// Save the session into the response /// /// Session to save /// Response to save into public void Save(ISession session, Response response) { if (session == null || !session.HasChanged) { return; } var sb = new StringBuilder(); foreach (var kvp in session) { sb.Append(HttpUtility.UrlEncode(kvp.Key)); sb.Append("="); var objectString = this.currentConfiguration.Serializer.Serialize(kvp.Value); sb.Append(HttpUtility.UrlEncode(objectString)); sb.Append(";"); } var cryptographyConfiguration = this.currentConfiguration.CryptographyConfiguration; var encryptedData = cryptographyConfiguration.EncryptionProvider.Encrypt(sb.ToString()); var hmacBytes = cryptographyConfiguration.HmacProvider.GenerateHmac(encryptedData); var cookieData = HttpUtility.UrlEncode(String.Format("{0}{1}", Convert.ToBase64String(hmacBytes), encryptedData)); var cookie = new NancyCookie(this.currentConfiguration.CookieName, cookieData, true) { Domain = this.currentConfiguration.Domain, Path = this.currentConfiguration.Path }; response.WithCookie(cookie); } /// /// Loads the session from the request /// /// Request to load from /// ISession containing the load session values public ISession Load(Request request) { var dictionary = new Dictionary(); var cookieName = this.currentConfiguration.CookieName; var hmacProvider = this.currentConfiguration.CryptographyConfiguration.HmacProvider; var encryptionProvider = this.currentConfiguration.CryptographyConfiguration.EncryptionProvider; string cookieValue; if (request.Cookies.TryGetValue(cookieName, out cookieValue)) { var cookieData = HttpUtility.UrlDecode(cookieValue); var hmacLength = Base64Helpers.GetBase64Length(hmacProvider.HmacLength); if (cookieData.Length < hmacLength) { return new Session(dictionary); } var hmacString = cookieData.Substring(0, hmacLength); var encryptedCookie = cookieData.Substring(hmacLength); var hmacBytes = Convert.FromBase64String(hmacString); var newHmac = hmacProvider.GenerateHmac(encryptedCookie); var hmacValid = HmacComparer.Compare(newHmac, hmacBytes, hmacProvider.HmacLength); var data = encryptionProvider.Decrypt(encryptedCookie); var parts = data.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts.Select(part => part.Split('=')).Where(part => part.Length == 2)) { var valueObject = this.currentConfiguration.Serializer.Deserialize(HttpUtility.UrlDecode(part[1])); dictionary[HttpUtility.UrlDecode(part[0])] = valueObject; } if (!hmacValid) { dictionary.Clear(); } } return new Session(dictionary); } /// /// Saves the request session into the response /// /// Nancy context /// Session store private static void SaveSession(NancyContext context, CookieBasedSessions sessionStore) { sessionStore.Save(context.Request.Session, context.Response); } /// /// Loads the request session /// /// Nancy context /// Session store /// Always returns null private static Response LoadSession(NancyContext context, CookieBasedSessions sessionStore) { if (context.Request == null) { return null; } context.Request.Session = sessionStore.Load(context.Request); return null; } } } ================================================ FILE: src/Nancy/Session/CookieBasedSessionsConfiguration.cs ================================================ namespace Nancy.Session { using Nancy.Cryptography; /// /// Configuration options for cookie based sessions /// public class CookieBasedSessionsConfiguration { internal const string DefaultCookieName = "_nc"; /// /// Initializes a new instance of the class. /// public CookieBasedSessionsConfiguration() : this(CryptographyConfiguration.Default) { } /// /// Initializes a new instance of the class. /// public CookieBasedSessionsConfiguration(CryptographyConfiguration cryptographyConfiguration) { CryptographyConfiguration = cryptographyConfiguration; CookieName = DefaultCookieName; } /// /// Gets or sets the cryptography configuration /// public CryptographyConfiguration CryptographyConfiguration { get; set; } /// /// Formatter for de/serializing the session objects /// public IObjectSerializer Serializer { get; set; } /// /// Cookie name for storing session information /// public string CookieName { get; set; } /// /// Gets or sets the domain of the session cookie /// public string Domain { get; set; } /// /// Gets or sets the path of the session cookie /// public string Path { get; set; } /// /// Gets a value indicating whether the configuration is valid or not. /// public virtual bool IsValid { get { if (string.IsNullOrEmpty(this.CookieName)) { return false; } if (this.Serializer == null) { return false; } if (this.CryptographyConfiguration == null) { return false; } if (this.CryptographyConfiguration.EncryptionProvider == null) { return false; } if (this.CryptographyConfiguration.HmacProvider == null) { return false; } return true; } } } } ================================================ FILE: src/Nancy/Session/ISession.cs ================================================ namespace Nancy.Session { using System.Collections.Generic; /// /// Defines the interface for a session /// public interface ISession : IEnumerable> { /// /// The number of session values /// /// The count of sessions int Count { get; } /// /// Deletes the session and all associated information /// void DeleteAll(); /// /// Deletes the specific key from the session /// void Delete(string key); /// /// Retrieves the value from the session /// object this[string key] { get; set; } /// /// Gets a value indicating whether this instance has changed. /// /// /// if this instance has changed; otherwise, . /// bool HasChanged { get; } } } ================================================ FILE: src/Nancy/Session/NullSessionProvider.cs ================================================ namespace Nancy.Session { using System; using System.Collections; using System.Collections.Generic; /// /// Provides a dummy session instance with no functionality. /// /// public class NullSessionProvider : ISession { /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator> GetEnumerator() { throw new InvalidOperationException("Session support is not enabled."); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// The number of session values /// /// public int Count { get { throw new InvalidOperationException("Session support is not enabled."); } } /// /// Deletes the session and all associated information /// public void DeleteAll() { throw new InvalidOperationException("Session support is not enabled."); } /// /// Deletes the specific key from the session /// public void Delete(string key) { throw new InvalidOperationException("Session support is not enabled."); } /// /// Retrieves the value from the session /// public object this[string key] { get { throw new InvalidOperationException("Session support is not enabled."); } set { throw new InvalidOperationException("Session support is not enabled."); } } /// /// Gets a value indicating whether this instance has changed. /// /// /// if this instance has changed; otherwise, . /// public bool HasChanged { get { return false; } } } } ================================================ FILE: src/Nancy/Session/Session.cs ================================================ namespace Nancy.Session { using System; using System.Collections; using System.Collections.Generic; /// /// Session implementation /// public class Session : ISession { private readonly IDictionary dictionary; private bool hasChanged; /// /// Initializes a new instance of the class. /// public Session() : this(new Dictionary(0)){} /// /// Initializes a new instance of the class, with /// the provided . /// /// The dictionary. public Session(IDictionary dictionary) { this.dictionary = dictionary; } /// /// Gets the number of items stored /// public int Count { get { return dictionary.Count; } } /// /// Deletes all items /// public void DeleteAll() { if (Count > 0) { MarkAsChanged(); } dictionary.Clear(); } /// /// Delete an item with the given key /// /// Key to delete public void Delete(string key) { if (dictionary.Remove(key)) { MarkAsChanged(); } } /// /// Gets or sets values /// /// The key whos value to get or set /// The value, or null or the key didn't exist public object this[string key] { get { object value; return dictionary.TryGetValue(key, out value) ? value : null; } set { var existingValue = this[key] ?? new Object(); if (existingValue.Equals(value)) { return; } dictionary[key] = value; MarkAsChanged(); } } /// /// Gets whether the session has changed /// public bool HasChanged { get { return this.hasChanged; } } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator> GetEnumerator() { return dictionary.GetEnumerator(); } private void MarkAsChanged() { hasChanged = true; } } } ================================================ FILE: src/Nancy/StaticConfiguration.cs ================================================ namespace Nancy { using System; using System.Diagnostics; using System.Linq; using Nancy.Bootstrapper; using Nancy.Diagnostics; /// /// Static configurations. /// public static class StaticConfiguration { static StaticConfiguration() { CaseSensitive = false; RequestQueryFormMultipartLimit = 1000; AllowFileStreamUploadAsync = true; } /// /// Gets or sets a value indicating whether or not to enable case sensitivity in query, parameters (DynamicDictionary) and model binding. Enable this to conform with RFC3986. /// [Description("Enable case sensitivity in query, parameters (DynamicDictionary) and model binding. Enable this to conform with RFC3986.")] public static bool CaseSensitive { get; set; } /// /// Gets or sets the limit on the number of query string variables, form fields, /// or multipart sections in a request. /// public static int RequestQueryFormMultipartLimit { get; set; } /// /// Gets or sets a value indicating whether or not to disable request stream switching /// public static bool? DisableRequestStreamSwitching { get; set; } /// /// Gets or sets a value indicating whether this allow file stream /// upload async due to mono issues before v4. Uploads of over 80mb would result in extra padded chars to the filestream corrupting the file. /// /// true if allow file stream upload async; otherwise, false. public static bool AllowFileStreamUploadAsync { get; set; } } } ================================================ FILE: src/Nancy/StaticContent.cs ================================================ namespace Nancy { using System; using System.Linq; using Nancy.Bootstrapper; using Nancy.Conventions; /// /// Registers the static contents hook in the application pipeline at startup. /// public class StaticContent : IApplicationStartup { private static IRootPathProvider rootPathProvider; private static StaticContentsConventions conventions; /// /// Initializes a new instance of the class, using the /// provided and . /// /// The current root path provider. /// The static content conventions. public StaticContent(IRootPathProvider rootPathProvider, StaticContentsConventions conventions) { StaticContent.rootPathProvider = rootPathProvider; StaticContent.conventions = conventions; } /// /// Perform any initialisation tasks /// public void Initialize(IPipelines pipelines) { } /// /// Enable "manual" static content. /// Only use this if you want to manually configure a pipeline hook to have static /// content server, for example, after authentication. /// /// The pipelines to hook into public static void Enable(IPipelines pipelines) { var item = new PipelineItem>("Static content", ctx => { return conventions .Select(convention => convention.Invoke(ctx, rootPathProvider.GetRootPath())) .FirstOrDefault(response => response != null); }); pipelines.BeforeRequest.AddItemToStartOfPipeline(item); } } } ================================================ FILE: src/Nancy/StaticContentConfiguration.cs ================================================ namespace Nancy { using System.Collections.Generic; /// /// Static content configuration. /// public class StaticContentConfiguration { /// /// Initializes a new instance of the class. /// /// A set of safe paths to retrieve static content from public StaticContentConfiguration(IEnumerable safePaths) { this.SafePaths = safePaths; } /// /// Gets the safe paths to retrieve static content from. /// /// Safe paths to retrieve static content from public IEnumerable SafePaths { get; private set; } } } ================================================ FILE: src/Nancy/StaticContentConfigurationExtensions.cs ================================================ namespace Nancy { using System.Collections.Generic; using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class StaticContentConfigurationExtensions { /// /// Configures /// /// An that should be configured. /// Paths that the application consider safe to return static content from public static void StaticContent(this INancyEnvironment environment, params string[] safepaths) { environment.AddValue(new StaticContentConfiguration( safePaths: safepaths)); } } } ================================================ FILE: src/Nancy/TinyIoc/TinyIoC.cs ================================================ #pragma warning disable CS1591, CS1574, CS1711, CS1712 // Disable XML comment related warnings //=============================================================================== // TinyIoC // // An easy to use, hassle free, Inversion of Control Container for small projects // and beginners alike. // // https://github.com/grumpydev/TinyIoC //=============================================================================== // Copyright © Steven Robbins. All rights reserved. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE. //=============================================================================== #region Preprocessor Directives // Uncomment this line if you want the container to automatically // register the TinyMessenger messenger/event aggregator //#define TINYMESSENGER // Uncomment this line if you want to internalize this library //#define TINYIOC_INTERNAL // Uncomment this line if you want to target PCL. //#define PORTABLE // Preprocessor directives for enabling/disabling functionality // depending on platform features. If the platform has an appropriate // #DEFINE then these should be set automatically below. #define EXPRESSIONS // Platform supports System.Linq.Expressions #define COMPILED_EXPRESSIONS // Platform supports compiling expressions #define APPDOMAIN_GETASSEMBLIES // Platform supports getting all assemblies from the AppDomain object #define UNBOUND_GENERICS_GETCONSTRUCTORS // Platform supports GetConstructors on unbound generic types #define GETPARAMETERS_OPEN_GENERICS // Platform supports GetParameters on open generics #define RESOLVE_OPEN_GENERICS // Platform supports resolving open generics #define READER_WRITER_LOCK_SLIM // Platform supports ReaderWriterLockSlim #define SERIALIZABLE // Platform supports SerializableAttribute/SerializationInfo/StreamingContext #if PORTABLE #undef APPDOMAIN_GETASSEMBLIES #undef COMPILED_EXPRESSIONS #undef READER_WRITER_LOCK_SLIM #endif // CompactFramework / Windows Phone 7 // By default does not support System.Linq.Expressions. // AppDomain object does not support enumerating all assemblies in the app domain. #if PocketPC || WINDOWS_PHONE #undef EXPRESSIONS #undef COMPILED_EXPRESSIONS #undef APPDOMAIN_GETASSEMBLIES #undef UNBOUND_GENERICS_GETCONSTRUCTORS #endif // PocketPC has a bizarre limitation on enumerating parameters on unbound generic methods. // We need to use a slower workaround in that case. #if PocketPC #undef GETPARAMETERS_OPEN_GENERICS #undef RESOLVE_OPEN_GENERICS #undef READER_WRITER_LOCK_SLIM #endif #if SILVERLIGHT #undef APPDOMAIN_GETASSEMBLIES #endif #if NETFX_CORE #undef APPDOMAIN_GETASSEMBLIES #undef RESOLVE_OPEN_GENERICS #endif #if COMPILED_EXPRESSIONS #define USE_OBJECT_CONSTRUCTOR #endif #if NETSTANDARD2_0 #undef SERIALIZABLE #undef APPDOMAIN_GETASSEMBLIES #endif #endregion #if SERIALIZABLE using System.Runtime.Serialization; #endif namespace Nancy.TinyIoc { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; #if EXPRESSIONS using System.Linq.Expressions; using System.Threading; #endif #if NETFX_CORE using System.Threading.Tasks; using Windows.Storage.Search; using Windows.Storage; using Windows.UI.Xaml.Shapes; #endif #region SafeDictionary #if READER_WRITER_LOCK_SLIM #if TINYIOC_INTERNAL internal #else public #endif class SafeDictionary : IDisposable { private readonly ReaderWriterLockSlim _padlock = new ReaderWriterLockSlim(); private readonly Dictionary _Dictionary = new Dictionary(); public TValue this[TKey key] { set { _padlock.EnterWriteLock(); try { TValue current; if (_Dictionary.TryGetValue(key, out current)) { var disposable = current as IDisposable; if (disposable != null) disposable.Dispose(); } _Dictionary[key] = value; } finally { _padlock.ExitWriteLock(); } } } public bool TryGetValue(TKey key, out TValue value) { _padlock.EnterReadLock(); try { return _Dictionary.TryGetValue(key, out value); } finally { _padlock.ExitReadLock(); } } public bool Remove(TKey key) { _padlock.EnterWriteLock(); try { return _Dictionary.Remove(key); } finally { _padlock.ExitWriteLock(); } } public void Clear() { _padlock.EnterWriteLock(); try { _Dictionary.Clear(); } finally { _padlock.ExitWriteLock(); } } public IEnumerable Keys { get { _padlock.EnterReadLock(); try { return new List(_Dictionary.Keys); } finally { _padlock.ExitReadLock(); } } } #region IDisposable Members public void Dispose() { _padlock.EnterWriteLock(); try { var disposableItems = from item in _Dictionary.Values where item is IDisposable select item as IDisposable; foreach (var item in disposableItems) { item.Dispose(); } } finally { _padlock.ExitWriteLock(); } GC.SuppressFinalize(this); } #endregion } #else #if TINYIOC_INTERNAL internal #else public #endif class SafeDictionary : IDisposable { private readonly object _Padlock = new object(); private readonly Dictionary _Dictionary = new Dictionary(); public TValue this[TKey key] { set { lock (_Padlock) { TValue current; if (_Dictionary.TryGetValue(key, out current)) { var disposable = current as IDisposable; if (disposable != null) disposable.Dispose(); } _Dictionary[key] = value; } } } public bool TryGetValue(TKey key, out TValue value) { lock (_Padlock) { return _Dictionary.TryGetValue(key, out value); } } public bool Remove(TKey key) { lock (_Padlock) { return _Dictionary.Remove(key); } } public void Clear() { lock (_Padlock) { _Dictionary.Clear(); } } public IEnumerable Keys { get { return _Dictionary.Keys; } } #region IDisposable Members public void Dispose() { lock (_Padlock) { var disposableItems = from item in _Dictionary.Values where item is IDisposable select item as IDisposable; foreach (var item in disposableItems) { item.Dispose(); } } GC.SuppressFinalize(this); } #endregion } #endif #endregion #region Extensions #if TINYIOC_INTERNAL internal #else public #endif static class AssemblyExtensions { public static Type[] SafeGetTypes(this Assembly assembly) { Type[] assemblies; try { assemblies = assembly.GetTypes(); } catch (System.IO.FileNotFoundException) { assemblies = ArrayCache.Empty(); } catch (NotSupportedException) { assemblies = ArrayCache.Empty(); } #if !NETFX_CORE catch (ReflectionTypeLoadException e) { assemblies = e.Types.Where(t => t != null).ToArray(); } #endif return assemblies; } } #if TINYIOC_INTERNAL internal #else public #endif static class TypeExtensions { private static SafeDictionary _genericMethodCache; static TypeExtensions() { _genericMethodCache = new SafeDictionary(); } //#if NETFX_CORE // /// // /// Gets a generic method from a type given the method name, generic types and parameter types // /// // /// Source type // /// Name of the method // /// Generic types to use to make the method generic // /// Method parameters // /// MethodInfo or null if no matches found // /// // /// // public static MethodInfo GetGenericMethod(this Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes) // { // MethodInfo method; // var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); // // Shouldn't need any additional locking // // we don't care if we do the method info generation // // more than once before it gets cached. // if (!_genericMethodCache.TryGetValue(cacheKey, out method)) // { // method = GetMethod(sourceType, methodName, genericTypes, parameterTypes); // _genericMethodCache[cacheKey] = method; // } // return method; // } //#else /// /// Gets a generic method from a type given the method name, binding flags, generic types and parameter types /// /// Source type /// Binding flags /// Name of the method /// Generic types to use to make the method generic /// Method parameters /// MethodInfo or null if no matches found /// /// public static MethodInfo GetGenericMethod(this Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) { MethodInfo method; var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); // Shouldn't need any additional locking // we don't care if we do the method info generation // more than once before it gets cached. if (!_genericMethodCache.TryGetValue(cacheKey, out method)) { method = GetMethod(sourceType, bindingFlags, methodName, genericTypes, parameterTypes); _genericMethodCache[cacheKey] = method; } return method; } //#endif #if NETFX_CORE private static MethodInfo GetMethod(Type sourceType, BindingFlags flags, string methodName, Type[] genericTypes, Type[] parameterTypes) { var methods = sourceType.GetMethods(flags).Where( mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( mi => mi.MakeGenericMethod(genericTypes)).Where( mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); if (methods.Count > 1) { throw new AmbiguousMatchException(); } return methods.FirstOrDefault(); } #else private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) { #if GETPARAMETERS_OPEN_GENERICS var methods = sourceType.GetMethods(bindingFlags).Where( mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( mi => mi.MakeGenericMethod(genericTypes)).Where( mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); #else var validMethods = from method in sourceType.GetMethods(bindingFlags) where method.Name == methodName where method.IsGenericMethod where method.GetGenericArguments().Length == genericTypes.Length let genericMethod = method.MakeGenericMethod(genericTypes) where genericMethod.GetParameters().Count() == parameterTypes.Length where genericMethod.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes) select genericMethod; var methods = validMethods.ToList(); #endif if (methods.Count > 1) { throw new AmbiguousMatchException(); } return methods.FirstOrDefault(); } #endif private sealed class GenericMethodCacheKey { private readonly Type _sourceType; private readonly string _methodName; private readonly Type[] _genericTypes; private readonly Type[] _parameterTypes; private readonly int _hashCode; public GenericMethodCacheKey(Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes) { _sourceType = sourceType; _methodName = methodName; _genericTypes = genericTypes; _parameterTypes = parameterTypes; _hashCode = GenerateHashCode(); } public override bool Equals(object obj) { var cacheKey = obj as GenericMethodCacheKey; if (cacheKey == null) return false; if (_sourceType != cacheKey._sourceType) return false; if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.Ordinal)) return false; if (_genericTypes.Length != cacheKey._genericTypes.Length) return false; if (_parameterTypes.Length != cacheKey._parameterTypes.Length) return false; for (int i = 0; i < _genericTypes.Length; ++i) { if (_genericTypes[i] != cacheKey._genericTypes[i]) return false; } for (int i = 0; i < _parameterTypes.Length; ++i) { if (_parameterTypes[i] != cacheKey._parameterTypes[i]) return false; } return true; } public override int GetHashCode() { return _hashCode; } private int GenerateHashCode() { unchecked { var result = _sourceType.GetHashCode(); result = (result * 397) ^ _methodName.GetHashCode(); for (int i = 0; i < _genericTypes.Length; ++i) { result = (result * 397) ^ _genericTypes[i].GetHashCode(); } for (int i = 0; i < _parameterTypes.Length; ++i) { result = (result * 397) ^ _parameterTypes[i].GetHashCode(); } return result; } } } } // @mbrit - 2012-05-22 - shim for ForEach call on List... #if NETFX_CORE internal static class ListExtender { internal static void ForEach(this List list, Action callback) { foreach (T obj in list) callback(obj); } } #endif #endregion #region TinyIoC Exception Types #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCResolutionException : Exception { private const string ERROR_TEXT = "Unable to resolve type: {0}"; public TinyIoCResolutionException(Type type) : base(String.Format(ERROR_TEXT, type.FullName)) { } public TinyIoCResolutionException(Type type, Exception innerException) : base(String.Format(ERROR_TEXT, type.FullName), innerException) { } #if SERIALIZABLE protected TinyIoCResolutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCRegistrationTypeException : Exception { private const string REGISTER_ERROR_TEXT = "Cannot register type {0} - abstract classes or interfaces are not valid implementation types for {1}."; public TinyIoCRegistrationTypeException(Type type, string factory) : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory)) { } public TinyIoCRegistrationTypeException(Type type, string factory, Exception innerException) : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory), innerException) { } #if SERIALIZABLE protected TinyIoCRegistrationTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCRegistrationException : Exception { private const string CONVERT_ERROR_TEXT = "Cannot convert current registration of {0} to {1}"; private const string GENERIC_CONSTRAINT_ERROR_TEXT = "Type {1} is not valid for a registration of type {0}"; public TinyIoCRegistrationException(Type type, string method) : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method)) { } public TinyIoCRegistrationException(Type type, string method, Exception innerException) : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method), innerException) { } public TinyIoCRegistrationException(Type registerType, Type implementationType) : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName)) { } public TinyIoCRegistrationException(Type registerType, Type implementationType, Exception innerException) : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName), innerException) { } #if SERIALIZABLE protected TinyIoCRegistrationException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCWeakReferenceException : Exception { private const string ERROR_TEXT = "Unable to instantiate {0} - referenced object has been reclaimed"; public TinyIoCWeakReferenceException(Type type) : base(String.Format(ERROR_TEXT, type.FullName)) { } public TinyIoCWeakReferenceException(Type type, Exception innerException) : base(String.Format(ERROR_TEXT, type.FullName), innerException) { } #if SERIALIZABLE protected TinyIoCWeakReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCConstructorResolutionException : Exception { private const string ERROR_TEXT = "Unable to resolve constructor for {0} using provided Expression."; public TinyIoCConstructorResolutionException(Type type) : base(String.Format(ERROR_TEXT, type.FullName)) { } public TinyIoCConstructorResolutionException(Type type, Exception innerException) : base(String.Format(ERROR_TEXT, type.FullName), innerException) { } public TinyIoCConstructorResolutionException(string message, Exception innerException) : base(message, innerException) { } public TinyIoCConstructorResolutionException(string message) : base(message) { } #if SERIALIZABLE protected TinyIoCConstructorResolutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } #if SERIALIZABLE [Serializable] #endif #if TINYIOC_INTERNAL internal #else public #endif class TinyIoCAutoRegistrationException : Exception { private const string ERROR_TEXT = "Duplicate implementation of type {0} found ({1})."; public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types) : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types))) { } public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types, Exception innerException) : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types)), innerException) { } #if SERIALIZABLE protected TinyIoCAutoRegistrationException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif private static string GetTypesString(IEnumerable types) { var typeNames = from type in types select type.FullName; return string.Join(",", typeNames.ToArray()); } } #endregion #region Public Setup / Settings Classes /// /// Name/Value pairs for specifying "user" parameters when resolving /// #if TINYIOC_INTERNAL internal #else public #endif sealed class NamedParameterOverloads : Dictionary { public static NamedParameterOverloads FromIDictionary(IDictionary data) { return data as NamedParameterOverloads ?? new NamedParameterOverloads(data); } public NamedParameterOverloads() { } public NamedParameterOverloads(IDictionary data) : base(data) { } private static readonly NamedParameterOverloads _Default = new NamedParameterOverloads(); public static NamedParameterOverloads Default { get { return _Default; } } } #if TINYIOC_INTERNAL internal #else public #endif enum UnregisteredResolutionActions { /// /// Attempt to resolve type, even if the type isn't registered. /// /// Registered types/options will always take precedence. /// AttemptResolve, /// /// Fail resolution if type not explicitly registered /// Fail, /// /// Attempt to resolve unregistered type if requested type is generic /// and no registration exists for the specific generic parameters used. /// /// Registered types/options will always take precedence. /// GenericsOnly } #if TINYIOC_INTERNAL internal #else public #endif enum NamedResolutionFailureActions { AttemptUnnamedResolution, Fail } #if TINYIOC_INTERNAL internal #else public #endif enum DuplicateImplementationActions { RegisterSingle, RegisterMultiple, Fail } /// /// Resolution settings /// #if TINYIOC_INTERNAL internal #else public #endif sealed class ResolveOptions { private static readonly ResolveOptions _Default = new ResolveOptions(); private static readonly ResolveOptions _FailUnregisteredAndNameNotFound = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; private static readonly ResolveOptions _FailUnregisteredOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; private static readonly ResolveOptions _FailNameNotFoundOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve }; private UnregisteredResolutionActions _UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve; public UnregisteredResolutionActions UnregisteredResolutionAction { get { return _UnregisteredResolutionAction; } set { _UnregisteredResolutionAction = value; } } private NamedResolutionFailureActions _NamedResolutionFailureAction = NamedResolutionFailureActions.Fail; public NamedResolutionFailureActions NamedResolutionFailureAction { get { return _NamedResolutionFailureAction; } set { _NamedResolutionFailureAction = value; } } /// /// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found) /// public static ResolveOptions Default { get { return _Default; } } /// /// Preconfigured option for attempting resolution of unregistered types and failing on named resolution if name not found /// public static ResolveOptions FailNameNotFoundOnly { get { return _FailNameNotFoundOnly; } } /// /// Preconfigured option for failing on resolving unregistered types and on named resolution if name not found /// public static ResolveOptions FailUnregisteredAndNameNotFound { get { return _FailUnregisteredAndNameNotFound; } } /// /// Preconfigured option for failing on resolving unregistered types, but attempting unnamed resolution if name not found /// public static ResolveOptions FailUnregisteredOnly { get { return _FailUnregisteredOnly; } } } #endregion #if TINYIOC_INTERNAL internal #else public #endif sealed partial class TinyIoCContainer : IDisposable { #region Fake NETFX_CORE Classes #if NETFX_CORE private sealed class MethodAccessException : Exception { } private sealed class AppDomain { public static AppDomain CurrentDomain { get; private set; } static AppDomain() { CurrentDomain = new AppDomain(); } // @mbrit - 2012-05-30 - in WinRT, this should be done async... public async Task> GetAssembliesAsync() { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; List assemblies = new List(); var files = await folder.GetFilesAsync(); foreach (StorageFile file in files) { if (file.FileType == ".dll" || file.FileType == ".exe") { AssemblyName name = new AssemblyName() { Name = System.IO.Path.GetFileNameWithoutExtension(file.Name) }; try { var asm = Assembly.Load(name); assemblies.Add(asm); } catch { // ignore exceptions here... } } } return assemblies; } } #endif #endregion #region "Fluent" API /// /// Registration options for "fluent" API /// public sealed class RegisterOptions { private TinyIoCContainer _Container; private TypeRegistration _Registration; public RegisterOptions(TinyIoCContainer container, TypeRegistration registration) { _Container = container; _Registration = registration; } /// /// Make registration a singleton (single instance) if possible /// /// RegisterOptions /// public RegisterOptions AsSingleton() { var currentFactory = _Container.GetCurrentFactory(_Registration); if (currentFactory == null) throw new TinyIoCRegistrationException(_Registration.Type, "singleton"); return _Container.AddUpdateRegistration(_Registration, currentFactory.SingletonVariant); } /// /// Make registration multi-instance if possible /// /// RegisterOptions /// public RegisterOptions AsMultiInstance() { var currentFactory = _Container.GetCurrentFactory(_Registration); if (currentFactory == null) throw new TinyIoCRegistrationException(_Registration.Type, "multi-instance"); return _Container.AddUpdateRegistration(_Registration, currentFactory.MultiInstanceVariant); } /// /// Make registration hold a weak reference if possible /// /// RegisterOptions /// public RegisterOptions WithWeakReference() { var currentFactory = _Container.GetCurrentFactory(_Registration); if (currentFactory == null) throw new TinyIoCRegistrationException(_Registration.Type, "weak reference"); return _Container.AddUpdateRegistration(_Registration, currentFactory.WeakReferenceVariant); } /// /// Make registration hold a strong reference if possible /// /// RegisterOptions /// public RegisterOptions WithStrongReference() { var currentFactory = _Container.GetCurrentFactory(_Registration); if (currentFactory == null) throw new TinyIoCRegistrationException(_Registration.Type, "strong reference"); return _Container.AddUpdateRegistration(_Registration, currentFactory.StrongReferenceVariant); } #if EXPRESSIONS public RegisterOptions UsingConstructor(Expression> constructor) { var lambda = constructor as LambdaExpression; if (lambda == null) throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); var newExpression = lambda.Body as NewExpression; if (newExpression == null) throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); var constructorInfo = newExpression.Constructor; if (constructorInfo == null) throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); var currentFactory = _Container.GetCurrentFactory(_Registration); if (currentFactory == null) throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); currentFactory.SetConstructor(constructorInfo); return this; } #endif /// /// Switches to a custom lifetime manager factory if possible. /// /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. /// /// RegisterOptions instance /// Custom lifetime manager /// Error string to display if switch fails /// RegisterOptions public static RegisterOptions ToCustomLifetimeManager(RegisterOptions instance, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { if (instance == null) throw new ArgumentNullException("instance", "instance is null."); if (lifetimeProvider == null) throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); if (string.IsNullOrEmpty(errorString)) throw new ArgumentException("errorString is null or empty.", "errorString"); var currentFactory = instance._Container.GetCurrentFactory(instance._Registration); if (currentFactory == null) throw new TinyIoCRegistrationException(instance._Registration.Type, errorString); return instance._Container.AddUpdateRegistration(instance._Registration, currentFactory.GetCustomObjectLifetimeVariant(lifetimeProvider, errorString)); } } /// /// Registration options for "fluent" API when registering multiple implementations /// public sealed class MultiRegisterOptions { private IEnumerable _RegisterOptions; /// /// Initializes a new instance of the MultiRegisterOptions class. /// /// Registration options public MultiRegisterOptions(IEnumerable registerOptions) { _RegisterOptions = registerOptions; } /// /// Make registration a singleton (single instance) if possible /// /// RegisterOptions /// public MultiRegisterOptions AsSingleton() { _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton()); return this; } /// /// Make registration multi-instance if possible /// /// MultiRegisterOptions /// public MultiRegisterOptions AsMultiInstance() { _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance()); return this; } /// /// Switches to a custom lifetime manager factory if possible. /// /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. /// /// MultiRegisterOptions instance /// Custom lifetime manager /// Error string to display if switch fails /// MultiRegisterOptions public static MultiRegisterOptions ToCustomLifetimeManager( MultiRegisterOptions instance, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { if (instance == null) throw new ArgumentNullException("instance", "instance is null."); if (lifetimeProvider == null) throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); if (string.IsNullOrEmpty(errorString)) throw new ArgumentException("errorString is null or empty.", "errorString"); instance._RegisterOptions = instance.ExecuteOnAllRegisterOptions(ro => RegisterOptions.ToCustomLifetimeManager(ro, lifetimeProvider, errorString)); return instance; } private IEnumerable ExecuteOnAllRegisterOptions(Func action) { var newRegisterOptions = new List(); foreach (var registerOption in _RegisterOptions) { newRegisterOptions.Add(action(registerOption)); } return newRegisterOptions; } } #endregion #region Public API #region Child Containers public TinyIoCContainer GetChildContainer() { return new TinyIoCContainer(this); } #endregion #region Registration /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// /// If more than one class implements an interface then only one implementation will be registered /// although no error will be thrown. /// public void AutoRegister() { #if APPDOMAIN_GETASSEMBLIES AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, null); #else AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, null); #endif } /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// Types will only be registered if they pass the supplied registration predicate. /// /// If more than one class implements an interface then only one implementation will be registered /// although no error will be thrown. /// /// Predicate to determine if a particular type should be registered public void AutoRegister(Func registrationPredicate) { #if APPDOMAIN_GETASSEMBLIES AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, registrationPredicate); #else AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, registrationPredicate); #endif } /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// /// What action to take when encountering duplicate implementations of an interface/base class. /// public void AutoRegister(DuplicateImplementationActions duplicateAction) { #if APPDOMAIN_GETASSEMBLIES AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, null); #else AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, null); #endif } /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. /// Types will only be registered if they pass the supplied registration predicate. /// /// What action to take when encountering duplicate implementations of an interface/base class. /// Predicate to determine if a particular type should be registered /// public void AutoRegister(DuplicateImplementationActions duplicateAction, Func registrationPredicate) { #if APPDOMAIN_GETASSEMBLIES AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, registrationPredicate); #else AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, registrationPredicate); #endif } /// /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies /// /// If more than one class implements an interface then only one implementation will be registered /// although no error will be thrown. /// /// Assemblies to process public void AutoRegister(IEnumerable assemblies) { AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, null); } /// /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies /// Types will only be registered if they pass the supplied registration predicate. /// /// If more than one class implements an interface then only one implementation will be registered /// although no error will be thrown. /// /// Assemblies to process /// Predicate to determine if a particular type should be registered public void AutoRegister(IEnumerable assemblies, Func registrationPredicate) { AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, registrationPredicate); } /// /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies /// /// Assemblies to process /// What action to take when encountering duplicate implementations of an interface/base class. /// public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction) { AutoRegisterInternal(assemblies, duplicateAction, null); } /// /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies /// Types will only be registered if they pass the supplied registration predicate. /// /// Assemblies to process /// What action to take when encountering duplicate implementations of an interface/base class. /// Predicate to determine if a particular type should be registered /// public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) { AutoRegisterInternal(assemblies, duplicateAction, registrationPredicate); } /// /// Creates/replaces a container class registration with default options. /// /// Type to register /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType) { return RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerType)); } /// /// Creates/replaces a named container class registration with default options. /// /// Type to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, string name) { return RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerType)); } /// /// Creates/replaces a container class registration with a given implementation and default options. /// /// Type to register /// Type to instantiate that implements RegisterType /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Type registerImplementation) { return this.RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerImplementation)); } /// /// Creates/replaces a named container class registration with a given implementation and default options. /// /// Type to register /// Type to instantiate that implements RegisterType /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Type registerImplementation, string name) { return this.RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerImplementation)); } /// /// Creates/replaces a container class registration with a specific, strong referenced, instance. /// /// Type to register /// Instance of RegisterType to register /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, object instance) { return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerType, instance)); } /// /// Creates/replaces a named container class registration with a specific, strong referenced, instance. /// /// Type to register /// Instance of RegisterType to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, object instance, string name) { return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerType, instance)); } /// /// Creates/replaces a container class registration with a specific, strong referenced, instance. /// /// Type to register /// Type of instance to register that implements RegisterType /// Instance of RegisterImplementation to register /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Type registerImplementation, object instance) { return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerImplementation, instance)); } /// /// Creates/replaces a named container class registration with a specific, strong referenced, instance. /// /// Type to register /// Type of instance to register that implements RegisterType /// Instance of RegisterImplementation to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Type registerImplementation, object instance, string name) { return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerImplementation, instance)); } /// /// Creates/replaces a container class registration with a user specified factory /// /// Type to register /// Factory/lambda that returns an instance of RegisterType /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Func factory) { return RegisterInternal(registerType, string.Empty, new DelegateFactory(registerType, factory)); } /// /// Creates/replaces a container class registration with a user specified factory /// /// Type to register /// Factory/lambda that returns an instance of RegisterType /// Name of registation /// RegisterOptions for fluent API public RegisterOptions Register(Type registerType, Func factory, string name) { return RegisterInternal(registerType, name, new DelegateFactory(registerType, factory)); } /// /// Creates/replaces a container class registration with default options. /// /// Type to register /// RegisterOptions for fluent API public RegisterOptions Register() where RegisterType : class { return this.Register(typeof(RegisterType)); } /// /// Creates/replaces a named container class registration with default options. /// /// Type to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(string name) where RegisterType : class { return this.Register(typeof(RegisterType), name); } /// /// Creates/replaces a container class registration with a given implementation and default options. /// /// Type to register /// Type to instantiate that implements RegisterType /// RegisterOptions for fluent API public RegisterOptions Register() where RegisterType : class where RegisterImplementation : class, RegisterType { return this.Register(typeof(RegisterType), typeof(RegisterImplementation)); } /// /// Creates/replaces a named container class registration with a given implementation and default options. /// /// Type to register /// Type to instantiate that implements RegisterType /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(string name) where RegisterType : class where RegisterImplementation : class, RegisterType { return this.Register(typeof(RegisterType), typeof(RegisterImplementation), name); } /// /// Creates/replaces a container class registration with a specific, strong referenced, instance. /// /// Type to register /// Instance of RegisterType to register /// RegisterOptions for fluent API public RegisterOptions Register(RegisterType instance) where RegisterType : class { return this.Register(typeof(RegisterType), instance); } /// /// Creates/replaces a named container class registration with a specific, strong referenced, instance. /// /// Type to register /// Instance of RegisterType to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(RegisterType instance, string name) where RegisterType : class { return this.Register(typeof(RegisterType), instance, name); } /// /// Creates/replaces a container class registration with a specific, strong referenced, instance. /// /// Type to register /// Type of instance to register that implements RegisterType /// Instance of RegisterImplementation to register /// RegisterOptions for fluent API public RegisterOptions Register(RegisterImplementation instance) where RegisterType : class where RegisterImplementation : class, RegisterType { return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance); } /// /// Creates/replaces a named container class registration with a specific, strong referenced, instance. /// /// Type to register /// Type of instance to register that implements RegisterType /// Instance of RegisterImplementation to register /// Name of registration /// RegisterOptions for fluent API public RegisterOptions Register(RegisterImplementation instance, string name) where RegisterType : class where RegisterImplementation : class, RegisterType { return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance, name); } /// /// Creates/replaces a container class registration with a user specified factory /// /// Type to register /// Factory/lambda that returns an instance of RegisterType /// RegisterOptions for fluent API public RegisterOptions Register(Func factory) where RegisterType : class { if (factory == null) { throw new ArgumentNullException("factory"); } return this.Register(typeof(RegisterType), (c, o) => factory(c, o)); } /// /// Creates/replaces a named container class registration with a user specified factory /// /// Type to register /// Factory/lambda that returns an instance of RegisterType /// Name of registation /// RegisterOptions for fluent API public RegisterOptions Register(Func factory, string name) where RegisterType : class { if (factory == null) { throw new ArgumentNullException("factory"); } return this.Register(typeof(RegisterType), (c, o) => factory(c, o), name); } /// /// Register multiple implementations of a type. /// /// Internally this registers each implementation using the full name of the class as its registration name. /// /// Type that each implementation implements /// Types that implement RegisterType /// MultiRegisterOptions for the fluent API public MultiRegisterOptions RegisterMultiple(IEnumerable implementationTypes) { return RegisterMultiple(typeof(RegisterType), implementationTypes); } /// /// Register multiple implementations of a type. /// /// Internally this registers each implementation using the full name of the class as its registration name. /// /// Type that each implementation implements /// Types that implement RegisterType /// MultiRegisterOptions for the fluent API public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable implementationTypes) { if (implementationTypes == null) throw new ArgumentNullException("types", "types is null."); foreach (var type in implementationTypes) //#if NETFX_CORE // if (!registrationType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) //#else if (!registrationType.IsAssignableFrom(type)) //#endif throw new ArgumentException(String.Format("types: The type {0} is not assignable from {1}", registrationType.FullName, type.FullName)); if (implementationTypes.Count() != implementationTypes.Distinct().Count()) { var queryForDuplicatedTypes = from i in implementationTypes group i by i into j where j.Count() > 1 select j.Key.FullName; var fullNamesOfDuplicatedTypes = string.Join(",\n", queryForDuplicatedTypes.ToArray()); var multipleRegMessage = string.Format("types: The same implementation type cannot be specified multiple times for {0}\n\n{1}", registrationType.FullName, fullNamesOfDuplicatedTypes); throw new ArgumentException(multipleRegMessage); } var registerOptions = new List(); foreach (var type in implementationTypes) { registerOptions.Add(Register(registrationType, type, type.FullName)); } return new MultiRegisterOptions(registerOptions); } #endregion #region Unregistration /// /// Remove a container class registration. /// /// Type to unregister /// true if the registration is successfully found and removed; otherwise, false. public bool Unregister() { return Unregister(typeof(RegisterType), string.Empty); } /// /// Remove a named container class registration. /// /// Type to unregister /// Name of registration /// true if the registration is successfully found and removed; otherwise, false. public bool Unregister(string name) { return Unregister(typeof(RegisterType), name); } /// /// Remove a container class registration. /// /// Type to unregister /// true if the registration is successfully found and removed; otherwise, false. public bool Unregister(Type registerType) { return Unregister(registerType, string.Empty); } /// /// Remove a named container class registration. /// /// Type to unregister /// Name of registration /// true if the registration is successfully found and removed; otherwise, false. public bool Unregister(Type registerType, string name) { var typeRegistration = new TypeRegistration(registerType, name); return RemoveRegistration(typeRegistration); } #endregion #region Resolution /// /// Attempts to resolve a type using default options. /// /// Type to resolve /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType) { return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); } /// /// Attempts to resolve a type using specified options. /// /// Type to resolve /// Resolution options /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, ResolveOptions options) { return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); } /// /// Attempts to resolve a type using default options and the supplied name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, string name) { return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); } /// /// Attempts to resolve a type using supplied options and name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// Resolution options /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, string name, ResolveOptions options) { return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); } /// /// Attempts to resolve a type using default options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, NamedParameterOverloads parameters) { return ResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); } /// /// Attempts to resolve a type using specified options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Resolution options /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) { return ResolveInternal(new TypeRegistration(resolveType), parameters, options); } /// /// Attempts to resolve a type using default options and the supplied constructor parameters and name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Name of registration /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters) { return ResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); } /// /// Attempts to resolve a named type using specified options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolution options /// Instance of type /// Unable to resolve the type. public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) { return ResolveInternal(new TypeRegistration(resolveType, name), parameters, options); } /// /// Attempts to resolve a type using default options. /// /// Type to resolve /// Instance of type /// Unable to resolve the type. public ResolveType Resolve() where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType)); } /// /// Attempts to resolve a type using specified options. /// /// Type to resolve /// Resolution options /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(ResolveOptions options) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), options); } /// /// Attempts to resolve a type using default options and the supplied name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(string name) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), name); } /// /// Attempts to resolve a type using supplied options and name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// Resolution options /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(string name, ResolveOptions options) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), name, options); } /// /// Attempts to resolve a type using default options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(NamedParameterOverloads parameters) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), parameters); } /// /// Attempts to resolve a type using specified options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Resolution options /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(NamedParameterOverloads parameters, ResolveOptions options) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), parameters, options); } /// /// Attempts to resolve a type using default options and the supplied constructor parameters and name. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// User specified constructor parameters /// Name of registration /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(string name, NamedParameterOverloads parameters) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), name, parameters); } /// /// Attempts to resolve a named type using specified options and the supplied constructor parameters. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolution options /// Instance of type /// Unable to resolve the type. public ResolveType Resolve(string name, NamedParameterOverloads parameters, ResolveOptions options) where ResolveType : class { return (ResolveType)Resolve(typeof(ResolveType), name, parameters, options); } /// /// Attempts to predict whether a given type can be resolved with default options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType) { return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); } /// /// Attempts to predict whether a given named type can be resolved with default options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// Bool indicating whether the type can be resolved private bool CanResolve(Type resolveType, string name) { return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); } /// /// Attempts to predict whether a given type can be resolved with the specified options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, ResolveOptions options) { return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); } /// /// Attempts to predict whether a given named type can be resolved with the specified options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, string name, ResolveOptions options) { return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); } /// /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// User supplied named parameter overloads /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, NamedParameterOverloads parameters) { return CanResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); } /// /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// User supplied named parameter overloads /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters) { return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); } /// /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// User supplied named parameter overloads /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) { return CanResolveInternal(new TypeRegistration(resolveType), parameters, options); } /// /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// User supplied named parameter overloads /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) { return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, options); } /// /// Attempts to predict whether a given type can be resolved with default options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Bool indicating whether the type can be resolved public bool CanResolve() where ResolveType : class { return CanResolve(typeof(ResolveType)); } /// /// Attempts to predict whether a given named type can be resolved with default options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Bool indicating whether the type can be resolved public bool CanResolve(string name) where ResolveType : class { return CanResolve(typeof(ResolveType), name); } /// /// Attempts to predict whether a given type can be resolved with the specified options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(ResolveOptions options) where ResolveType : class { return CanResolve(typeof(ResolveType), options); } /// /// Attempts to predict whether a given named type can be resolved with the specified options. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(string name, ResolveOptions options) where ResolveType : class { return CanResolve(typeof(ResolveType), name, options); } /// /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// User supplied named parameter overloads /// Bool indicating whether the type can be resolved public bool CanResolve(NamedParameterOverloads parameters) where ResolveType : class { return CanResolve(typeof(ResolveType), parameters); } /// /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// User supplied named parameter overloads /// Bool indicating whether the type can be resolved public bool CanResolve(string name, NamedParameterOverloads parameters) where ResolveType : class { return CanResolve(typeof(ResolveType), name, parameters); } /// /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// User supplied named parameter overloads /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(NamedParameterOverloads parameters, ResolveOptions options) where ResolveType : class { return CanResolve(typeof(ResolveType), parameters, options); } /// /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. /// /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. /// /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. /// /// Type to resolve /// Name of registration /// User supplied named parameter overloads /// Resolution options /// Bool indicating whether the type can be resolved public bool CanResolve(string name, NamedParameterOverloads parameters, ResolveOptions options) where ResolveType : class { return CanResolve(typeof(ResolveType), name, parameters, options); } /// /// Attemps to resolve a type using the default options /// /// Type to resolve /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, out object resolvedType) { try { resolvedType = Resolve(resolveType); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the given options /// /// Type to resolve /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, ResolveOptions options, out object resolvedType) { try { resolvedType = Resolve(resolveType, options); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the default options and given name /// /// Type to resolve /// Name of registration /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, string name, out object resolvedType) { try { resolvedType = Resolve(resolveType, name); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the given options and name /// /// Type to resolve /// Name of registration /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, string name, ResolveOptions options, out object resolvedType) { try { resolvedType = Resolve(resolveType, name, options); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the default options and supplied constructor parameters /// /// Type to resolve /// User specified constructor parameters /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, out object resolvedType) { try { resolvedType = Resolve(resolveType, parameters); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the default options and supplied name and constructor parameters /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, out object resolvedType) { try { resolvedType = Resolve(resolveType, name, parameters); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the supplied options and constructor parameters /// /// Type to resolve /// User specified constructor parameters /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) { try { resolvedType = Resolve(resolveType, parameters, options); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the supplied name, options and constructor parameters /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) { try { resolvedType = Resolve(resolveType, name, parameters, options); return true; } catch (TinyIoCResolutionException) { resolvedType = null; return false; } } /// /// Attemps to resolve a type using the default options /// /// Type to resolve /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the given options /// /// Type to resolve /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(ResolveOptions options, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(options); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the default options and given name /// /// Type to resolve /// Name of registration /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(string name, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(name); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the given options and name /// /// Type to resolve /// Name of registration /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(string name, ResolveOptions options, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(name, options); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the default options and supplied constructor parameters /// /// Type to resolve /// User specified constructor parameters /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(NamedParameterOverloads parameters, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(parameters); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the default options and supplied name and constructor parameters /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(string name, NamedParameterOverloads parameters, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(name, parameters); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the supplied options and constructor parameters /// /// Type to resolve /// User specified constructor parameters /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(parameters, options); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Attemps to resolve a type using the supplied name, options and constructor parameters /// /// Type to resolve /// Name of registration /// User specified constructor parameters /// Resolution options /// Resolved type or default if resolve fails /// True if resolved sucessfully, false otherwise public bool TryResolve(string name, NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) where ResolveType : class { try { resolvedType = Resolve(name, parameters, options); return true; } catch (TinyIoCResolutionException) { resolvedType = default(ResolveType); return false; } } /// /// Returns all registrations of a type /// /// Type to resolveAll /// Whether to include un-named (default) registrations /// IEnumerable public IEnumerable ResolveAll(Type resolveType, bool includeUnnamed) { return ResolveAllInternal(resolveType, includeUnnamed); } /// /// Returns all registrations of a type, both named and unnamed /// /// Type to resolveAll /// IEnumerable public IEnumerable ResolveAll(Type resolveType) { return ResolveAll(resolveType, false); } /// /// Returns all registrations of a type /// /// Type to resolveAll /// Whether to include un-named (default) registrations /// IEnumerable public IEnumerable ResolveAll(bool includeUnnamed) where ResolveType : class { return this.ResolveAll(typeof(ResolveType), includeUnnamed).Cast(); } /// /// Returns all registrations of a type, both named and unnamed /// /// Type to resolveAll /// IEnumerable public IEnumerable ResolveAll() where ResolveType : class { return ResolveAll(true); } /// /// Attempts to resolve all public property dependencies on the given object. /// /// Object to "build up" public void BuildUp(object input) { BuildUpInternal(input, ResolveOptions.Default); } /// /// Attempts to resolve all public property dependencies on the given object using the given resolve options. /// /// Object to "build up" /// Resolve options to use public void BuildUp(object input, ResolveOptions resolveOptions) { BuildUpInternal(input, resolveOptions); } #endregion #endregion #region Object Factories /// /// Provides custom lifetime management for ASP.Net per-request lifetimes etc. /// public interface ITinyIoCObjectLifetimeProvider { /// /// Gets the stored object if it exists, or null if not /// /// Object instance or null object GetObject(); /// /// Store the object /// /// Object to store void SetObject(object value); /// /// Release the object /// void ReleaseObject(); } private abstract class ObjectFactoryBase { /// /// Whether to assume this factory sucessfully constructs its objects /// /// Generally set to true for delegate style factories as CanResolve cannot delve /// into the delegates they contain. /// public virtual bool AssumeConstruction { get { return false; } } /// /// The type the factory instantiates /// public abstract Type CreatesType { get; } /// /// Constructor to use, if specified /// public ConstructorInfo Constructor { get; protected set; } /// /// Create the type /// /// Type user requested to be resolved /// Container that requested the creation /// Any user parameters passed /// /// public abstract object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options); public virtual ObjectFactoryBase SingletonVariant { get { throw new TinyIoCRegistrationException(this.GetType(), "singleton"); } } public virtual ObjectFactoryBase MultiInstanceVariant { get { throw new TinyIoCRegistrationException(this.GetType(), "multi-instance"); } } public virtual ObjectFactoryBase StrongReferenceVariant { get { throw new TinyIoCRegistrationException(this.GetType(), "strong reference"); } } public virtual ObjectFactoryBase WeakReferenceVariant { get { throw new TinyIoCRegistrationException(this.GetType(), "weak reference"); } } public virtual ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { throw new TinyIoCRegistrationException(this.GetType(), errorString); } public virtual void SetConstructor(ConstructorInfo constructor) { Constructor = constructor; } public virtual ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) { return this; } } /// /// IObjectFactory that creates new instances of types for each resolution /// private class MultiInstanceFactory : ObjectFactoryBase { private readonly Type registerType; private readonly Type registerImplementation; public override Type CreatesType { get { return this.registerImplementation; } } public MultiInstanceFactory(Type registerType, Type registerImplementation) { //#if NETFX_CORE // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) // throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); //#else if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); //#endif if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); this.registerType = registerType; this.registerImplementation = registerImplementation; } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { try { return container.ConstructType(requestedType, this.registerImplementation, Constructor, parameters, options); } catch (TinyIoCResolutionException ex) { throw new TinyIoCResolutionException(this.registerType, ex); } } public override ObjectFactoryBase SingletonVariant { get { return new SingletonFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); } public override ObjectFactoryBase MultiInstanceVariant { get { return this; } } } /// /// IObjectFactory that invokes a specified delegate to construct the object /// private class DelegateFactory : ObjectFactoryBase { private readonly Type registerType; private Func _factory; public override bool AssumeConstruction { get { return true; } } public override Type CreatesType { get { return this.registerType; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { try { return _factory.Invoke(container, parameters); } catch (Exception ex) { throw new TinyIoCResolutionException(this.registerType, ex); } } public DelegateFactory(Type registerType, Func factory) { if (factory == null) throw new ArgumentNullException("factory"); _factory = factory; this.registerType = registerType; } public override ObjectFactoryBase WeakReferenceVariant { get { return new WeakDelegateFactory(this.registerType, _factory); } } public override ObjectFactoryBase StrongReferenceVariant { get { return this; } } public override void SetConstructor(ConstructorInfo constructor) { throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); } } /// /// IObjectFactory that invokes a specified delegate to construct the object /// Holds the delegate using a weak reference /// private class WeakDelegateFactory : ObjectFactoryBase { private readonly Type registerType; private WeakReference _factory; public override bool AssumeConstruction { get { return true; } } public override Type CreatesType { get { return this.registerType; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { var factory = _factory.Target as Func; if (factory == null) throw new TinyIoCWeakReferenceException(this.registerType); try { return factory.Invoke(container, parameters); } catch (Exception ex) { throw new TinyIoCResolutionException(this.registerType, ex); } } public WeakDelegateFactory(Type registerType, Func factory) { if (factory == null) throw new ArgumentNullException("factory"); _factory = new WeakReference(factory); this.registerType = registerType; } public override ObjectFactoryBase StrongReferenceVariant { get { var factory = _factory.Target as Func; if (factory == null) throw new TinyIoCWeakReferenceException(this.registerType); return new DelegateFactory(this.registerType, factory); } } public override ObjectFactoryBase WeakReferenceVariant { get { return this; } } public override void SetConstructor(ConstructorInfo constructor) { throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); } } /// /// Stores an particular instance to return for a type /// private class InstanceFactory : ObjectFactoryBase, IDisposable { private readonly Type registerType; private readonly Type registerImplementation; private object _instance; public override bool AssumeConstruction { get { return true; } } public InstanceFactory(Type registerType, Type registerImplementation, object instance) { if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "InstanceFactory"); this.registerType = registerType; this.registerImplementation = registerImplementation; _instance = instance; } public override Type CreatesType { get { return this.registerImplementation; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { return _instance; } public override ObjectFactoryBase MultiInstanceVariant { get { return new MultiInstanceFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase WeakReferenceVariant { get { return new WeakInstanceFactory(this.registerType, this.registerImplementation, this._instance); } } public override ObjectFactoryBase StrongReferenceVariant { get { return this; } } public override void SetConstructor(ConstructorInfo constructor) { throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); } public void Dispose() { var disposable = _instance as IDisposable; if (disposable != null) disposable.Dispose(); } } /// /// Stores an particular instance to return for a type /// /// Stores the instance with a weak reference /// private class WeakInstanceFactory : ObjectFactoryBase, IDisposable { private readonly Type registerType; private readonly Type registerImplementation; private readonly WeakReference _instance; public WeakInstanceFactory(Type registerType, Type registerImplementation, object instance) { if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "WeakInstanceFactory"); this.registerType = registerType; this.registerImplementation = registerImplementation; _instance = new WeakReference(instance); } public override Type CreatesType { get { return this.registerImplementation; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { var instance = _instance.Target; if (instance == null) throw new TinyIoCWeakReferenceException(this.registerType); return instance; } public override ObjectFactoryBase MultiInstanceVariant { get { return new MultiInstanceFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase WeakReferenceVariant { get { return this; } } public override ObjectFactoryBase StrongReferenceVariant { get { var instance = _instance.Target; if (instance == null) throw new TinyIoCWeakReferenceException(this.registerType); return new InstanceFactory(this.registerType, this.registerImplementation, instance); } } public override void SetConstructor(ConstructorInfo constructor) { throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); } public void Dispose() { var disposable = _instance.Target as IDisposable; if (disposable != null) disposable.Dispose(); } } /// /// A factory that lazy instantiates a type and always returns the same instance /// private class SingletonFactory : ObjectFactoryBase, IDisposable { private readonly Type registerType; private readonly Type registerImplementation; private readonly object SingletonLock = new object(); private object _Current; public SingletonFactory(Type registerType, Type registerImplementation) { //#if NETFX_CORE // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) //#else if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) //#endif throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); this.registerType = registerType; this.registerImplementation = registerImplementation; } public override Type CreatesType { get { return this.registerImplementation; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { if (parameters.Count != 0) throw new ArgumentException("Cannot specify parameters for singleton types"); lock (SingletonLock) if (_Current == null) _Current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); return _Current; } public override ObjectFactoryBase SingletonVariant { get { return this; } } public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); } public override ObjectFactoryBase MultiInstanceVariant { get { return new MultiInstanceFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) { // We make sure that the singleton is constructed before the child container takes the factory. // Otherwise the results would vary depending on whether or not the parent container had resolved // the type before the child container does. GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); return this; } public void Dispose() { if (this._Current == null) return; var disposable = this._Current as IDisposable; if (disposable != null) disposable.Dispose(); } } /// /// A factory that offloads lifetime to an external lifetime provider /// private class CustomObjectLifetimeFactory : ObjectFactoryBase, IDisposable { private readonly object SingletonLock = new object(); private readonly Type registerType; private readonly Type registerImplementation; private readonly ITinyIoCObjectLifetimeProvider _LifetimeProvider; public CustomObjectLifetimeFactory(Type registerType, Type registerImplementation, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorMessage) { if (lifetimeProvider == null) throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); if (!IsValidAssignment(registerType, registerImplementation)) throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); //#if NETFX_CORE // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) //#else if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) //#endif throw new TinyIoCRegistrationTypeException(registerImplementation, errorMessage); this.registerType = registerType; this.registerImplementation = registerImplementation; _LifetimeProvider = lifetimeProvider; } public override Type CreatesType { get { return this.registerImplementation; } } public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) { object current; lock (SingletonLock) { current = _LifetimeProvider.GetObject(); if (current == null) { current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); _LifetimeProvider.SetObject(current); } } return current; } public override ObjectFactoryBase SingletonVariant { get { _LifetimeProvider.ReleaseObject(); return new SingletonFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase MultiInstanceVariant { get { _LifetimeProvider.ReleaseObject(); return new MultiInstanceFactory(this.registerType, this.registerImplementation); } } public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) { _LifetimeProvider.ReleaseObject(); return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); } public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) { // We make sure that the singleton is constructed before the child container takes the factory. // Otherwise the results would vary depending on whether or not the parent container had resolved // the type before the child container does. GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); return this; } public void Dispose() { _LifetimeProvider.ReleaseObject(); } } #endregion #region Singleton Container private static readonly TinyIoCContainer _Current = new TinyIoCContainer(); static TinyIoCContainer() { } /// /// Lazy created Singleton instance of the container for simple scenarios /// public static TinyIoCContainer Current { get { return _Current; } } #endregion #region Type Registrations public sealed class TypeRegistration { private int _hashCode; public Type Type { get; private set; } public string Name { get; private set; } public TypeRegistration(Type type) : this(type, string.Empty) { } public TypeRegistration(Type type, string name) { Type = type; Name = name; _hashCode = String.Concat(Type.FullName, "|", Name).GetHashCode(); } public override bool Equals(object obj) { var typeRegistration = obj as TypeRegistration; if (typeRegistration == null) return false; if (Type != typeRegistration.Type) return false; if (String.Compare(Name, typeRegistration.Name, StringComparison.Ordinal) != 0) return false; return true; } public override int GetHashCode() { return _hashCode; } } private readonly SafeDictionary _RegisteredTypes; #if USE_OBJECT_CONSTRUCTOR private delegate object ObjectConstructor(params object[] parameters); private static readonly SafeDictionary _ObjectConstructorCache = new SafeDictionary(); #endif #endregion #region Constructors public TinyIoCContainer() { _RegisteredTypes = new SafeDictionary(); RegisterDefaultTypes(); } TinyIoCContainer _Parent; private TinyIoCContainer(TinyIoCContainer parent) : this() { _Parent = parent; } #endregion #region Internal Methods private readonly object _AutoRegisterLock = new object(); private void AutoRegisterInternal(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) { lock (_AutoRegisterLock) { var types = assemblies.SelectMany(a => a.SafeGetTypes()).Where(t => !IsIgnoredType(t, registrationPredicate)).ToList(); var concreteTypes = types .Where(type => type.IsClass() && (type.IsAbstract() == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition()))) .ToList(); foreach (var type in concreteTypes) { try { RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, type)); } #if PORTABLE catch (MemberAccessException) #else catch (MethodAccessException) #endif { // Ignore methods we can't access - added for Silverlight } } var abstractInterfaceTypes = from type in types where ((type.IsInterface() || type.IsAbstract()) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition())) select type; foreach (var type in abstractInterfaceTypes) { var localType = type; var implementations = from implementationType in concreteTypes where localType.IsAssignableFrom(implementationType) select implementationType; if (implementations.Skip(1).Any()) { if (duplicateAction == DuplicateImplementationActions.Fail) throw new TinyIoCAutoRegistrationException(type, implementations); if (duplicateAction == DuplicateImplementationActions.RegisterMultiple) { RegisterMultiple(type, implementations); } } var firstImplementation = implementations.FirstOrDefault(); if (firstImplementation != null) { try { RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, firstImplementation)); } #if PORTABLE catch (MemberAccessException) #else catch (MethodAccessException) #endif { // Ignore methods we can't access - added for Silverlight } } } } } private bool IsIgnoredAssembly(Assembly assembly) { // TODO - find a better way to remove "system" assemblies from the auto registration var ignoreChecks = new List>() { asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal), asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal), asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal), asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal), asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal), asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal), asm => asm.FullName.StartsWith("xunit.", StringComparison.Ordinal), }; foreach (var check in ignoreChecks) { if (check(assembly)) return true; } return false; } private bool IsIgnoredType(Type type, Func registrationPredicate) { // TODO - find a better way to remove "system" types from the auto registration var ignoreChecks = new List>() { t => t.FullName.StartsWith("System.", StringComparison.Ordinal), t => t.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), t => t.IsPrimitive(), #if !UNBOUND_GENERICS_GETCONSTRUCTORS t => t.IsGenericTypeDefinition(), #endif t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface() || t.IsAbstract()), }; if (registrationPredicate != null) { ignoreChecks.Add(t => !registrationPredicate(t)); } foreach (var check in ignoreChecks) { if (check(type)) return true; } return false; } private void RegisterDefaultTypes() { Register(this); #if TINYMESSENGER // Only register the TinyMessenger singleton if we are the root container if (_Parent == null) Register(); #endif } private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration) { ObjectFactoryBase current = null; _RegisteredTypes.TryGetValue(registration, out current); return current; } private RegisterOptions RegisterInternal(Type registerType, string name, ObjectFactoryBase factory) { var typeRegistration = new TypeRegistration(registerType, name); return AddUpdateRegistration(typeRegistration, factory); } private RegisterOptions AddUpdateRegistration(TypeRegistration typeRegistration, ObjectFactoryBase factory) { _RegisteredTypes[typeRegistration] = factory; return new RegisterOptions(this, typeRegistration); } private bool RemoveRegistration(TypeRegistration typeRegistration) { return _RegisteredTypes.Remove(typeRegistration); } private ObjectFactoryBase GetDefaultObjectFactory(Type registerType, Type registerImplementation) { //#if NETFX_CORE // if (registerType.GetTypeInfo().IsInterface() || registerType.GetTypeInfo().IsAbstract()) //#else if (registerType.IsInterface() || registerType.IsAbstract()) //#endif return new SingletonFactory(registerType, registerImplementation); return new MultiInstanceFactory(registerType, registerImplementation); } private bool CanResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) { if (parameters == null) throw new ArgumentNullException("parameters"); Type checkType = registration.Type; string name = registration.Name; ObjectFactoryBase factory; if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType, name), out factory)) { if (factory.AssumeConstruction) return true; if (factory.Constructor == null) return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; else return CanConstruct(factory.Constructor, parameters, options); } #if RESOLVE_OPEN_GENERICS if (checkType.IsInterface() && checkType.IsGenericType()) { // if the type is registered as an open generic, then see if the open generic is registered if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType.GetGenericTypeDefinition(), name), out factory)) { if (factory.AssumeConstruction) return true; if (factory.Constructor == null) return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; else return CanConstruct(factory.Constructor, parameters, options); } } #endif // Fail if requesting named resolution and settings set to fail if unresolved // Or bubble up if we have a parent if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) return (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; // Attemped unnamed fallback container resolution if relevant and requested if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) { if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType), out factory)) { if (factory.AssumeConstruction) return true; return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; } } // Check if type is an automatic lazy factory request if (IsAutomaticLazyFactoryRequest(checkType)) return true; // Check if type is an IEnumerable if (IsIEnumerableRequest(registration.Type)) return true; // Attempt unregistered construction if possible and requested // If we cant', bubble if we have a parent if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) return (GetBestConstructor(checkType, parameters, options) != null) ? true : (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; // Bubble resolution up the container tree if we have a parent if (_Parent != null) return _Parent.CanResolveInternal(registration, parameters, options); return false; } private bool IsIEnumerableRequest(Type type) { if (!type.IsGenericType()) return false; Type genericType = type.GetGenericTypeDefinition(); if (genericType == typeof(IEnumerable<>)) return true; return false; } private bool IsAutomaticLazyFactoryRequest(Type type) { if (!type.IsGenericType()) return false; Type genericType = type.GetGenericTypeDefinition(); // Just a func if (genericType == typeof(Func<>)) return true; // 2 parameter func with string as first parameter (name) //#if NETFX_CORE // if ((genericType == typeof(Func<,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string))) //#else if ((genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))) //#endif return true; // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) //#if NETFX_CORE // if ((genericType == typeof(Func<,,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string) && type.GetTypeInfo().GenericTypeArguments[1] == typeof(IDictionary))) //#else if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) //#endif return true; return false; } private ObjectFactoryBase GetParentObjectFactory(TypeRegistration registration) { if (_Parent == null) return null; ObjectFactoryBase factory; if (_Parent._RegisteredTypes.TryGetValue(registration, out factory)) { return factory.GetFactoryForChildContainer(registration.Type, _Parent, this); } return _Parent.GetParentObjectFactory(registration); } private object ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) { ObjectFactoryBase factory; // Attempt container resolution if (_RegisteredTypes.TryGetValue(registration, out factory)) { try { return factory.GetObject(registration.Type, this, parameters, options); } catch (TinyIoCResolutionException) { throw; } catch (Exception ex) { throw new TinyIoCResolutionException(registration.Type, ex); } } #if RESOLVE_OPEN_GENERICS // Attempt container resolution of open generic if (registration.Type.IsGenericType()) { var openTypeRegistration = new TypeRegistration(registration.Type.GetGenericTypeDefinition(), registration.Name); if (_RegisteredTypes.TryGetValue(openTypeRegistration, out factory)) { try { return factory.GetObject(registration.Type, this, parameters, options); } catch (TinyIoCResolutionException) { throw; } catch (Exception ex) { throw new TinyIoCResolutionException(registration.Type, ex); } } } #endif // Attempt to get a factory from parent if we can var bubbledObjectFactory = GetParentObjectFactory(registration); if (bubbledObjectFactory != null) { try { return bubbledObjectFactory.GetObject(registration.Type, this, parameters, options); } catch (TinyIoCResolutionException) { throw; } catch (Exception ex) { throw new TinyIoCResolutionException(registration.Type, ex); } } // Fail if requesting named resolution and settings set to fail if unresolved if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) throw new TinyIoCResolutionException(registration.Type); // Attemped unnamed fallback container resolution if relevant and requested if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) { if (_RegisteredTypes.TryGetValue(new TypeRegistration(registration.Type, string.Empty), out factory)) { try { return factory.GetObject(registration.Type, this, parameters, options); } catch (TinyIoCResolutionException) { throw; } catch (Exception ex) { throw new TinyIoCResolutionException(registration.Type, ex); } } } #if EXPRESSIONS // Attempt to construct an automatic lazy factory if possible if (IsAutomaticLazyFactoryRequest(registration.Type)) return GetLazyAutomaticFactoryRequest(registration.Type); #endif if (IsIEnumerableRequest(registration.Type)) return GetIEnumerableRequest(registration.Type); // Attempt unregistered construction if possible and requested if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) { if (!registration.Type.IsAbstract() && !registration.Type.IsInterface()) return ConstructType(null, registration.Type, parameters, options); } // Unable to resolve - throw throw new TinyIoCResolutionException(registration.Type); } #if EXPRESSIONS private object GetLazyAutomaticFactoryRequest(Type type) { if (!type.IsGenericType()) return null; Type genericType = type.GetGenericTypeDefinition(); //#if NETFX_CORE // Type[] genericArguments = type.GetTypeInfo().GenericTypeArguments.ToArray(); //#else Type[] genericArguments = type.GetGenericArguments(); //#endif // Just a func if (genericType == typeof(Func<>)) { Type returnType = genericArguments[0]; //#if NETFX_CORE // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => !mi.GetParameters().Any()); //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", ArrayCache.Empty()); //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod); var resolveLambda = Expression.Lambda(resolveCall).Compile(); return resolveLambda; } // 2 parameter func with string as first parameter (name) if ((genericType == typeof(Func<,>)) && (genericArguments[0] == typeof(string))) { Type returnType = genericArguments[1]; //#if NETFX_CORE // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 1 && mi.GetParameters()[0].GetType() == typeof(String)); //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String) }); //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); ParameterExpression[] resolveParameters = new ParameterExpression[] { Expression.Parameter(typeof(String), "name") }; var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, resolveParameters); var resolveLambda = Expression.Lambda(resolveCall, resolveParameters).Compile(); return resolveLambda; } // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) //#if NETFX_CORE // if ((genericType == typeof(Func<,,>) && type.GenericTypeArguments[0] == typeof(string) && type.GenericTypeArguments[1] == typeof(IDictionary))) //#else if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) //#endif { Type returnType = genericArguments[2]; var name = Expression.Parameter(typeof(string), "name"); var parameters = Expression.Parameter(typeof(IDictionary), "parameters"); //#if NETFX_CORE // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 2 && mi.GetParameters()[0].GetType() == typeof(String) && mi.GetParameters()[1].GetType() == typeof(NamedParameterOverloads)); //#else MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String), typeof(NamedParameterOverloads) }); //#endif resolveMethod = resolveMethod.MakeGenericMethod(returnType); var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, name, Expression.Call(typeof(NamedParameterOverloads), "FromIDictionary", null, parameters)); var resolveLambda = Expression.Lambda(resolveCall, name, parameters).Compile(); return resolveLambda; } throw new TinyIoCResolutionException(type); } #endif private object GetIEnumerableRequest(Type type) { //#if NETFX_CORE // var genericResolveAllMethod = this.GetType().GetGenericMethod("ResolveAll", type.GenericTypeArguments, new[] { typeof(bool) }); //#else var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) }); //#endif return genericResolveAllMethod.Invoke(this, new object[] { false }); } private bool CanConstruct(ConstructorInfo ctor, NamedParameterOverloads parameters, ResolveOptions options) { if (parameters == null) throw new ArgumentNullException("parameters"); foreach (var parameter in ctor.GetParameters()) { if (string.IsNullOrEmpty(parameter.Name)) return false; var isParameterOverload = parameters.ContainsKey(parameter.Name); //#if NETFX_CORE // if (parameter.ParameterType.GetTypeInfo().IsPrimitive && !isParameterOverload) //#else if (parameter.ParameterType.IsPrimitive() && !isParameterOverload) //#endif return false; if (!isParameterOverload && !CanResolveInternal(new TypeRegistration(parameter.ParameterType), NamedParameterOverloads.Default, options)) return false; } return true; } private ConstructorInfo GetBestConstructor(Type type, NamedParameterOverloads parameters, ResolveOptions options) { if (parameters == null) throw new ArgumentNullException("parameters"); //#if NETFX_CORE // if (type.GetTypeInfo().IsValueType) //#else if (type.IsValueType()) //#endif return null; // Get constructors in reverse order based on the number of parameters // i.e. be as "greedy" as possible so we satify the most amount of dependencies possible var ctors = this.GetTypeConstructors(type); foreach (var ctor in ctors) { if (this.CanConstruct(ctor, parameters, options)) return ctor; } return null; } private IEnumerable GetTypeConstructors(Type type) { //#if NETFX_CORE // return type.GetTypeInfo().DeclaredConstructors.OrderByDescending(ctor => ctor.GetParameters().Count()); //#else return type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Count()); //#endif } private object ConstructType(Type requestedType, Type implementationType, ResolveOptions options) { return ConstructType(requestedType, implementationType, null, NamedParameterOverloads.Default, options); } private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, ResolveOptions options) { return ConstructType(requestedType, implementationType, constructor, NamedParameterOverloads.Default, options); } private object ConstructType(Type requestedType, Type implementationType, NamedParameterOverloads parameters, ResolveOptions options) { return ConstructType(requestedType, implementationType, null, parameters, options); } private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) { var typeToConstruct = implementationType; #if RESOLVE_OPEN_GENERICS if (implementationType.IsGenericTypeDefinition()) { if (requestedType == null || !requestedType.IsGenericType() || !requestedType.GetGenericArguments().Any()) throw new TinyIoCResolutionException(typeToConstruct); typeToConstruct = typeToConstruct.MakeGenericType(requestedType.GetGenericArguments()); } #endif if (constructor == null) { // Try and get the best constructor that we can construct // if we can't construct any then get the constructor // with the least number of parameters so we can throw a meaningful // resolve exception constructor = GetBestConstructor(typeToConstruct, parameters, options) ?? GetTypeConstructors(typeToConstruct).LastOrDefault(); } if (constructor == null) throw new TinyIoCResolutionException(typeToConstruct); var ctorParams = constructor.GetParameters(); object[] args = new object[ctorParams.Count()]; for (int parameterIndex = 0; parameterIndex < ctorParams.Count(); parameterIndex++) { var currentParam = ctorParams[parameterIndex]; try { object value; args[parameterIndex] = parameters.TryGetValue(currentParam.Name, out value) ? value : ResolveInternal( new TypeRegistration(currentParam.ParameterType), NamedParameterOverloads.Default, options); } catch (TinyIoCResolutionException ex) { // If a constructor parameter can't be resolved // it will throw, so wrap it and throw that this can't // be resolved. throw new TinyIoCResolutionException(typeToConstruct, ex); } catch (Exception ex) { throw new TinyIoCResolutionException(typeToConstruct, ex); } } try { #if USE_OBJECT_CONSTRUCTOR var constructionDelegate = CreateObjectConstructionDelegateWithCache(constructor); return constructionDelegate.Invoke(args); #else return constructor.Invoke(args); #endif } catch (Exception ex) { throw new TinyIoCResolutionException(typeToConstruct, ex); } } #if USE_OBJECT_CONSTRUCTOR private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) { ObjectConstructor objectConstructor; if (_ObjectConstructorCache.TryGetValue(constructor, out objectConstructor)) return objectConstructor; // We could lock the cache here, but there's no real side // effect to two threads creating the same ObjectConstructor // at the same time, compared to the cost of a lock for // every creation. var constructorParams = constructor.GetParameters(); var lambdaParams = Expression.Parameter(typeof(object[]), "parameters"); var newParams = new Expression[constructorParams.Length]; for (int i = 0; i < constructorParams.Length; i++) { var paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i)); newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType); } var newExpression = Expression.New(constructor, newParams); var constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams); objectConstructor = (ObjectConstructor)constructionLambda.Compile(); _ObjectConstructorCache[constructor] = objectConstructor; return objectConstructor; } #endif private void BuildUpInternal(object input, ResolveOptions resolveOptions) { //#if NETFX_CORE // var properties = from property in input.GetType().GetTypeInfo().DeclaredProperties // where (property.GetMethod != null) && (property.SetMethod != null) && !property.PropertyType.GetTypeInfo().IsValueType // select property; //#else var properties = from property in input.GetType().GetProperties() where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType() select property; //#endif foreach (var property in properties) { if (property.GetValue(input, null) == null) { try { property.SetValue(input, ResolveInternal(new TypeRegistration(property.PropertyType), NamedParameterOverloads.Default, resolveOptions), null); } catch (TinyIoCResolutionException) { // Catch any resolution errors and ignore them } } } } private IEnumerable GetParentRegistrationsForType(Type resolveType) { if (_Parent == null) return ArrayCache.Empty(); var registrations = _Parent._RegisteredTypes.Keys.Where(tr => tr.Type == resolveType); return registrations.Concat(_Parent.GetParentRegistrationsForType(resolveType)); } private IEnumerable ResolveAllInternal(Type resolveType, bool includeUnnamed) { var registrations = _RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(GetParentRegistrationsForType(resolveType)).Distinct(); if (!includeUnnamed) registrations = registrations.Where(tr => tr.Name != string.Empty); return registrations.Select(registration => this.ResolveInternal(registration, NamedParameterOverloads.Default, ResolveOptions.Default)); } private static bool IsValidAssignment(Type registerType, Type registerImplementation) { //#if NETFX_CORE // var registerTypeDef = registerType.GetTypeInfo(); // var registerImplementationDef = registerImplementation.GetTypeInfo(); // if (!registerTypeDef.IsGenericTypeDefinition) // { // if (!registerTypeDef.IsAssignableFrom(registerImplementationDef)) // return false; // } // else // { // if (registerTypeDef.IsInterface()) // { // if (!registerImplementationDef.ImplementedInterfaces.Any(t => t.GetTypeInfo().Name == registerTypeDef.Name)) // return false; // } // else if (registerTypeDef.IsAbstract() && registerImplementationDef.BaseType() != registerType) // { // return false; // } // } //#else if (!registerType.IsGenericTypeDefinition()) { if (!registerType.IsAssignableFrom(registerImplementation)) return false; } else { if (registerType.IsInterface()) { #if (PORTABLE || NETSTANDARD1_6) if (!registerImplementation.GetInterfaces().Any(t => t.Name == registerType.Name)) return false; #else if (!registerImplementation.FindInterfaces((t, o) => t.Name == registerType.Name, null).Any()) return false; #endif } else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType) { return false; } } //#endif return true; } #endregion #region IDisposable Members bool disposed = false; public void Dispose() { if (!disposed) { disposed = true; _RegisteredTypes.Dispose(); GC.SuppressFinalize(this); } } #endregion } #if NETSTANDARD2_0 static class ReverseTypeExtender { public static bool IsClass(this Type type) { return type.GetTypeInfo().IsClass; } public static bool IsAbstract(this Type type) { return type.GetTypeInfo().IsAbstract; } public static bool IsInterface(this Type type) { return type.GetTypeInfo().IsInterface; } public static bool IsPrimitive(this Type type) { return type.GetTypeInfo().IsPrimitive; } public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; } public static bool IsGenericType(this Type type) { return type.GetTypeInfo().IsGenericType; } public static bool IsGenericParameter(this Type type) { return type.IsGenericParameter; } public static bool IsGenericTypeDefinition(this Type type) { return type.GetTypeInfo().IsGenericTypeDefinition; } public static Type BaseType(this Type type) { return type.GetTypeInfo().BaseType; } public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; } } #endif // reverse shim for WinRT SR changes... #if (!NETFX_CORE && !NETSTANDARD2_0) static class ReverseTypeExtender { public static bool IsClass(this Type type) { return type.IsClass; } public static bool IsAbstract(this Type type) { return type.IsAbstract; } public static bool IsInterface(this Type type) { return type.IsInterface; } public static bool IsPrimitive(this Type type) { return type.IsPrimitive; } public static bool IsValueType(this Type type) { return type.IsValueType; } public static bool IsGenericType(this Type type) { return type.IsGenericType; } public static bool IsGenericParameter(this Type type) { return type.IsGenericParameter; } public static bool IsGenericTypeDefinition(this Type type) { return type.IsGenericTypeDefinition; } public static Type BaseType(this Type type) { return type.BaseType; } public static Assembly Assembly(this Type type) { return type.Assembly; } } #endif } ================================================ FILE: src/Nancy/TraceConfiguration.cs ================================================ namespace Nancy { /// /// Configuration for tracing. /// public class TraceConfiguration { /// /// Initializes a new instance of the class. /// /// Determines if tracing should be enabled. /// Determines if traces should be displayed in error messages. public TraceConfiguration(bool enabled, bool displayErrorTraces) { this.Enabled = enabled; this.DisplayErrorTraces = displayErrorTraces; } /// /// Gets a value indicating whether or not to enable request tracing. /// /// if tracing should be enabled, otherwise . public bool Enabled { get; private set; } /// /// Gets a value indicating whether or not to display traces in error messages. /// /// traces should be displayed in error messages, otherwise . public bool DisplayErrorTraces { get; private set; } } } ================================================ FILE: src/Nancy/TraceConfigurationExtensions.cs ================================================ namespace Nancy { using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class TraceConfigurationExtensions { /// /// Configures . /// /// An that should be configured. /// if tracing should be enabled, otherwise . /// traces should be displayed in error messages, otherwise . public static void Tracing(this INancyEnvironment environment, bool enabled, bool displayErrorTraces) { environment.AddValue(new TraceConfiguration( enabled: enabled, displayErrorTraces: displayErrorTraces)); } } } ================================================ FILE: src/Nancy/TypeCatalogExtensions.cs ================================================ namespace Nancy { using System; using System.Collections.Generic; /// /// Contains extension methods for implementations. /// public static class TypeCatalogExtensions { /// /// Gets all instances that are assignable to , using . /// /// The instance where the types should be retrieved from. /// The that all returned types should be assingable to. /// An of instances. public static IReadOnlyCollection GetTypesAssignableTo(this ITypeCatalog typeCatalog, Type type) { return typeCatalog.GetTypesAssignableTo(type, TypeResolveStrategies.All); } /// /// Gets all instances that are assignable to , using . /// /// The instance where the types should be retrieved from. /// The that all returned types should be assingable to. /// An of instances. public static IReadOnlyCollection GetTypesAssignableTo(this ITypeCatalog typeCatalog) { return typeCatalog.GetTypesAssignableTo(typeof(TType), TypeResolveStrategies.All); } /// /// Gets all types that are assignable to the provided . /// /// The instance where the types should be retrieved from. /// A that should be used then retrieving types. /// The that returned types should be assignable to. /// An of instances. public static IReadOnlyCollection GetTypesAssignableTo(this ITypeCatalog typeCatalog, TypeResolveStrategy strategy) { return typeCatalog.GetTypesAssignableTo(typeof(TType), strategy); } } } ================================================ FILE: src/Nancy/TypeResolveStrategies.cs ================================================ namespace Nancy { using System; using System.Reflection; using Nancy.Extensions; /// /// Default implementations. /// public class TypeResolveStrategies { /// /// Resolve types from all available locations. /// public static readonly TypeResolveStrategy All = type => { return true; }; /// /// Resolve types that are not located in the Nancy assembly. /// public static readonly TypeResolveStrategy ExcludeNancy = type => { return !OnlyNancy.Invoke(type); }; /// /// Resolve types that are not located in the Nancy namespace. /// public static readonly TypeResolveStrategy ExcludeNancyNamespace = type => { return !OnlyNancyNamespace.Invoke(type); }; /// /// Resolve types that are located in the Nancy assembly. /// public static readonly TypeResolveStrategy OnlyNancy = type => { return type.GetAssembly().Equals(typeof(INancyEngine).GetTypeInfo().Assembly); }; /// /// Resolve types that are located in the Nancy namespace. /// public static readonly TypeResolveStrategy OnlyNancyNamespace = type => { return (type.Namespace ?? string.Empty).StartsWith("Nancy", StringComparison.OrdinalIgnoreCase); }; } } ================================================ FILE: src/Nancy/TypeResolveStrategy.cs ================================================ namespace Nancy { using System; /// /// Predicate used to decide if a should be included when resolving types. /// /// The that is being inspected. /// if the type should be included in the result, otherwise . public delegate bool TypeResolveStrategy(Type type); } ================================================ FILE: src/Nancy/Url.cs ================================================ namespace Nancy { using System; using System.Net; using System.Net.Sockets; using System.Text; /// /// Represents a full Url of the form scheme://hostname:port/basepath/path?query /// /// Since this is for internal use, and fragments are not passed to the server, fragments are not supported. public sealed class Url { private string basePath; private string query; /// /// Initializes a new instance of the class. /// public Url() { this.Scheme = "http"; this.HostName = string.Empty; this.Port = null; this.BasePath = string.Empty; this.Path = string.Empty; this.Query = string.Empty; } /// /// Initializes a new instance of the class, with /// the provided . /// /// A containing a URL. public Url(string url) { var uri = new Uri(url); this.HostName = uri.Host; this.Path = uri.LocalPath; this.Port = uri.Port; this.Query = uri.Query; this.Scheme = uri.Scheme; } /// /// Gets or sets the HTTP protocol used by the client. /// /// The protocol. public string Scheme { get; set; } /// /// Gets the hostname of the request /// public string HostName { get; set; } /// /// Gets the port name of the request /// public int? Port { get; set; } /// /// Gets the base path of the request i.e. the "Nancy root" /// public string BasePath { get { return this.basePath; } set { this.basePath = (value ?? string.Empty).TrimEnd('/'); } } /// /// Gets the path of the request, relative to the base path /// This property drives the route matching /// public string Path { get; set; } /// /// Gets the querystring data of the requested resource. /// public string Query { get { return this.query; } set { this.query = GetQuery(value); } } /// /// Gets the domain part of the request /// public string SiteBase { get { return new StringBuilder() .Append(this.Scheme) .Append("://") .Append(GetHostName(this.HostName)) .Append(GetPort(this.Port)) .ToString(); } } /// /// Gets whether the url is secure or not. /// public bool IsSecure { get { return "https".Equals(this.Scheme, StringComparison.OrdinalIgnoreCase); } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return new StringBuilder() .Append(this.Scheme) .Append("://") .Append(GetHostName(this.HostName)) .Append(GetPort(this.Port)) .Append(GetCorrectPath(this.BasePath)) .Append(GetCorrectPath(this.Path)) .Append(this.Query) .ToString(); } /// /// Clones the url. /// /// Returns a new cloned instance of the url. public Url Clone() { return new Url { BasePath = this.BasePath, HostName = this.HostName, Port = this.Port, Query = this.Query, Path = this.Path, Scheme = this.Scheme }; } /// /// Casts the current instance to a instance. /// /// The instance that should be cast. /// A representation of the . public static implicit operator string(Url url) { return url.ToString(); } /// /// Casts the current instance to a instance. /// /// The instance that should be cast. /// An representation of the . public static implicit operator Url(string url) { return new Uri(url); } /// /// Casts the current instance to a instance. /// /// The instance that should be cast. /// An representation of the . public static implicit operator Uri(Url url) { return new Uri(url.ToString(), UriKind.Absolute); } /// /// Casts a instance to a instance /// /// The instance that should be cast. /// An representation of the . public static implicit operator Url(Uri uri) { if (uri.IsAbsoluteUri) { return new Url { HostName = uri.Host, Path = uri.LocalPath, Port = uri.Port, Query = uri.Query, Scheme = uri.Scheme }; } return new Url { Path = uri.OriginalString }; } private static string GetQuery(string query) { return string.IsNullOrEmpty(query) ? string.Empty : (query[0] == '?' ? query : '?' + query); } private static string GetCorrectPath(string path) { return (string.IsNullOrEmpty(path) || path.Equals("/")) ? string.Empty : path; } private static string GetPort(int? port) { return port.HasValue ? string.Concat(":", port.Value) : string.Empty; } private static string GetHostName(string hostName) { IPAddress address; if (IPAddress.TryParse(hostName, out address)) { var addressString = address.ToString(); return address.AddressFamily == AddressFamily.InterNetworkV6 ? string.Format("[{0}]", addressString) : addressString; } return hostName; } } } ================================================ FILE: src/Nancy/Validation/CompositeValidator.cs ================================================ namespace Nancy.Validation { using System; using System.Collections.Generic; using System.Linq; /// /// A composite validator to combine other validators. /// public class CompositeValidator : IModelValidator { private readonly IEnumerable validators; /// /// Initializes a new instance of the class. /// /// The validators. /// The type of the model that is being validated. public CompositeValidator(IEnumerable validators, Type modelType) { var modelValidators = validators.ToArray(); this.ModelType = modelType; this.Description = CreateCompositeDescription(modelValidators, modelType); this.validators = modelValidators; } /// /// Gets the description of the validator. /// public ModelValidationDescriptor Description { get; private set; } /// /// The type of the model that is being validated by the validator. /// public Type ModelType { get; private set; } /// /// Validates the specified instance. /// /// The instance that should be validated. /// The of the current request. /// A with the result of the validation. public ModelValidationResult Validate(object instance, NancyContext context) { var errors = validators .Select(v => v.Validate(instance, context)) .Where(r => r != null) .SelectMany(r => r.Errors) .ToDictionary(x => x.Key, x => x.Value); return (!errors.Any()) ? new ModelValidationResult() : new ModelValidationResult(errors); } private static ModelValidationDescriptor CreateCompositeDescription(IEnumerable validators, Type modelType) { var rules = validators .SelectMany(v => v.Description.Rules) .ToDictionary(x => x.Key, x => x.Value); return new ModelValidationDescriptor(rules, modelType); } } } ================================================ FILE: src/Nancy/Validation/DefaultValidatorLocator.cs ================================================ namespace Nancy.Validation { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; /// /// The default Nancy implementation of IValidatorLocator. /// public class DefaultValidatorLocator : IModelValidatorLocator { private readonly ConcurrentDictionary cachedValidators; private readonly IEnumerable factories; /// /// Initializes a new instance of the class. /// /// The factories. public DefaultValidatorLocator(IEnumerable factories) { this.cachedValidators = new ConcurrentDictionary(); this.factories = factories ?? Enumerable.Empty(); } /// /// Gets a validator for a given type. /// /// The type to validate. /// An instance or if none found. public IModelValidator GetValidatorForType(Type type) { if (!this.factories.Any()) { throw new ModelValidationException("No model validator factory could be located. Please ensure that you have an appropriate validation package installed, such as one of the Nancy.Validation packages."); } return cachedValidators.GetOrAdd(type, CreateValidator); } private IModelValidator CreateValidator(Type type) { var validators = this.factories .Select(factory => factory.Create(type)) .Where(validator => validator != null) .ToArray(); if(!validators.Any()) { return null; } return (validators.Length == 1) ? validators[0] : new CompositeValidator(validators, type); } } } ================================================ FILE: src/Nancy/Validation/IModelValidator.cs ================================================ namespace Nancy.Validation { using System; /// /// Provides a way to validate a type as well as a description to use for client-side validation. /// public interface IModelValidator { /// /// Gets the description of the validator. /// ModelValidationDescriptor Description { get; } /// /// Gets the of the model that is being validated by the validator. /// Type ModelType { get; } /// /// Validates the specified instance. /// /// The instance that should be validated. /// The of the current request. /// A with the result of the validation. ModelValidationResult Validate(object instance, NancyContext context); } } ================================================ FILE: src/Nancy/Validation/IModelValidatorFactory.cs ================================================ namespace Nancy.Validation { using System; /// /// Creates instances of IValidator. /// public interface IModelValidatorFactory { /// /// Creates a validator for the given type. /// /// The type. /// A validator for the given type or null if none exists. IModelValidator Create(Type type); } } ================================================ FILE: src/Nancy/Validation/IModelValidatorLocator.cs ================================================ namespace Nancy.Validation { using System; /// /// Locates a validator for a given type. /// public interface IModelValidatorLocator { /// /// Gets a validator for a given type. /// /// The type to validate. /// An instance or if none found. IModelValidator GetValidatorForType(Type type); } } ================================================ FILE: src/Nancy/Validation/ModelValidationDescriptor.cs ================================================ namespace Nancy.Validation { using System; using System.Collections.Generic; /// /// A description of the rules a validator provides. /// public class ModelValidationDescriptor { /// /// Initializes a new instance of the class. /// /// The rules that describes the model. /// The type of the model that the rules are defined for. public ModelValidationDescriptor(IEnumerable rules, Type modelType) : this(GetModelValidationRuleDictionary(rules), modelType) { } /// /// Initializes a new instance of the class. /// /// The rules that describes the model, grouped by member name. /// The type of the model that the rules are defined for. public ModelValidationDescriptor(IDictionary> rules, Type modelType) { this.Rules = rules; this.ModelType = modelType; } /// /// The type of the model that is being described. /// public Type ModelType { get; private set; } /// /// Gets the rules. /// /// An instance that contains instances grouped by property name. public IDictionary> Rules { get; private set; } private static IDictionary> GetModelValidationRuleDictionary(IEnumerable rules) { var results = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (rules == null) { return results; } foreach (var rule in rules) { foreach (var name in rule.MemberNames) { IList list; if (!results.TryGetValue(name, out list)) { list = new List(); results.Add(name, list); } list.Add(rule); } } return results; } } } ================================================ FILE: src/Nancy/Validation/ModelValidationError.cs ================================================ namespace Nancy.Validation { using System.Collections.Generic; /// /// Represents a model validation error. /// public class ModelValidationError { /// /// Initializes a new instance of the class. /// /// Name of the member that the error describes. /// public ModelValidationError(string memberName, string errorMessage) : this(new[] { memberName }, errorMessage) { } /// /// Initializes a new instance of the class. /// /// The member names that the error describes. /// public ModelValidationError(IEnumerable memberNames, string errorMessage) { this.MemberNames = memberNames; this.ErrorMessage = errorMessage; } /// /// Gets the member names that are a part of the error. /// /// An that contains the name of the members. public IEnumerable MemberNames { get; private set; } /// /// /// public string ErrorMessage { get; private set; } /// /// Implicitly cast a validation error to a string. /// /// The that should be cast. /// A containing the validation error description. public static implicit operator string(ModelValidationError error) { return error.ErrorMessage; } /// /// Returns the . /// /// A string containing the error message. public override string ToString() { return this.ErrorMessage; } } } ================================================ FILE: src/Nancy/Validation/ModelValidationException.cs ================================================ namespace Nancy.Validation { using System; /// /// Exception that is thrown during problems with model validation. /// public class ModelValidationException : Exception { /// /// Initializes a new instance of the class. /// public ModelValidationException() { } /// /// Initializes a new instance of the class, /// with the provided . /// /// The error message that explains the reason for the exception. public ModelValidationException(string message) : base(message) { } /// /// Initializes a new instance of the class, /// with the provided and /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. public ModelValidationException(string message, Exception innerException) : base(message, innerException) { } } } ================================================ FILE: src/Nancy/Validation/ModelValidationResult.cs ================================================ namespace Nancy.Validation { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; /// /// Represents the result of a model validation. /// [DebuggerDisplay("IsValid = {IsValid}")] public class ModelValidationResult { /// /// Initializes a new instance of the class. /// public ModelValidationResult() : this(Enumerable.Empty()) { } /// /// Initializes a new instance of the class. /// /// The instances that makes up the result. public ModelValidationResult(IEnumerable errors) : this(GetModelValidationErrorDictionary((errors ?? Enumerable.Empty()).ToArray())) { } /// /// Initializes a new instance of the class. /// /// The instances that makes up the result, grouped by member name. public ModelValidationResult(IDictionary> errors) { this.Errors = errors; } /// /// Gets the errors. /// /// An instance that contains instances grouped by property name. public IDictionary> Errors { get; set; } /// /// Gets a clean representation of the errors. /// /// public IEnumerable FormattedErrors { get { var result = this.Errors.Select(x => new {Key = x.Key, Errors = x.Value.Select(y => y.ErrorMessage).ToArray()}); return result; } } /// /// Gets a value indicating whether the validated instance is valid or not. /// /// if the validated instance is valid; otherwise, . public bool IsValid { get { return !Errors.Keys.Any(); } } private static IDictionary> GetModelValidationErrorDictionary(ModelValidationError[] results) { var output = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (results == null || !results.Any()) { return output; } foreach (var result in results) { IList value; foreach (var name in result.MemberNames) { if (!output.TryGetValue(name, out value)) { value = new List(); output.Add(name, value); } value.Add(result); } if (!result.MemberNames.Any() && !string.IsNullOrEmpty(result.ErrorMessage)) { if (!output.TryGetValue(string.Empty, out value)) { value = new List(); output.Add(string.Empty, value); } value.Add(result); } } return output; } } } ================================================ FILE: src/Nancy/Validation/ModelValidationRule.cs ================================================ namespace Nancy.Validation { using System; using System.Collections.Generic; /// /// A description of a validation rule. /// public class ModelValidationRule { private readonly Func errorMessageFormatter; /// /// Initializes a new instance of the class. /// /// Type of the rule. /// The error message formatter. public ModelValidationRule(string ruleType, Func errorMessageFormatter) { if (ruleType == null) { throw new ArgumentNullException("ruleType"); } if (errorMessageFormatter == null) { throw new ArgumentNullException("errorMessageFormatter"); } this.RuleType = ruleType; this.errorMessageFormatter = errorMessageFormatter; } /// /// Initializes a new instance of the class. /// /// Type of the rule. /// The error message formatter. /// Name of the member. public ModelValidationRule(string ruleType, Func errorMessageFormatter, IEnumerable memberNames) : this(ruleType, errorMessageFormatter) { this.MemberNames = memberNames; } /// /// Gets the names of the members this rule validates. /// /// An that contains the name of the member. public IEnumerable MemberNames { get; private set; } /// /// Gets the type of the rule. /// /// The type of the rule. public string RuleType { get; private set; } /// /// Gets the error message that this rule will provide upon error. /// /// The name. /// The error message. public string GetErrorMessage(string name) { return errorMessageFormatter(name); } } } ================================================ FILE: src/Nancy/Validation/ModuleExtensions.cs ================================================ namespace Nancy.Validation { using System.Linq; /// /// Extensions to for validation. /// public static class ModuleExtensions { /// /// Performs validation on the specified . /// /// The type of the that is being validated. /// The module that the validation is performed from. /// The instance that is being validated. /// A instance. public static ModelValidationResult Validate(this INancyModule module, T instance) { var validator = module.ValidatorLocator.GetValidatorForType(typeof(T)); var result = (validator == null) ? new ModelValidationResult() : validator.Validate(instance, module.Context); if (module.ModelValidationResult.Errors.Any()) { module.ModelValidationResult.Errors = module.ModelValidationResult.Errors.Concat(result.Errors).ToDictionary(key => key.Key, val => val.Value); } module.ModelValidationResult = module.ModelValidationResult.Errors.Any() ? module.ModelValidationResult : result; return module.ModelValidationResult; } } } ================================================ FILE: src/Nancy/Validation/Rules/ComparisonOperator.cs ================================================ namespace Nancy.Validation.Rules { /// /// Specifies the validation comparison operators used by the type. /// public enum ComparisonOperator { /// /// A comparison for greater than. /// GreaterThan, /// /// A comparison for greater than or equal to. /// GreaterThanOrEqual, /// /// A comparison for less than. /// LessThan, /// /// A comparison for less than or equal to. /// LessThanOrEqual, /// /// A comparison for equality. /// Equal, /// /// A comparison for inequality. /// NotEqual } } ================================================ FILE: src/Nancy/Validation/Rules/ComparisonValidationRule.cs ================================================ namespace Nancy.Validation.Rules { using System; using System.Collections.Generic; /// /// Implementation of for comparing two values using a /// provided . /// public class ComparisonValidationRule : ModelValidationRule { /// /// Initializes a new instance of the class. /// /// The error message formatter. /// The member names. /// The that should be used when comparing values. /// Gets the value to compare against. public ComparisonValidationRule(Func errorMessageFormatter, IEnumerable memberNames, ComparisonOperator @operator, object value) : base("Comparison", errorMessageFormatter, memberNames) { this.Operator = @operator; this.Value = value; } /// /// The that should be used when comparing values. /// /// A value. public ComparisonOperator Operator { get; private set; } /// /// Gets the value to compare against. /// public object Value { get; private set; } } } ================================================ FILE: src/Nancy/Validation/Rules/NotEmptyValidationRule.cs ================================================ namespace Nancy.Validation.Rules { using System; using System.Collections.Generic; /// /// Implementation of for ensuring a string does not /// contain an empty value. /// public class NotEmptyValidationRule : ModelValidationRule { /// /// Initializes a new instance of the class. /// /// The error message formatter. /// The member names. public NotEmptyValidationRule(Func errorMessageFormatter, IEnumerable memberNames) : base("NotEmpty", errorMessageFormatter, memberNames) { } } } ================================================ FILE: src/Nancy/Validation/Rules/NotNullValidationRule.cs ================================================ namespace Nancy.Validation.Rules { using System; using System.Collections.Generic; /// /// Implementation of for ensuring a string is not null. /// public class NotNullValidationRule : ModelValidationRule { /// /// Initializes a new instance of the class. /// /// The error message formatter. /// The member names. public NotNullValidationRule(Func errorMessageFormatter, IEnumerable memberNames) : base("NotNull", errorMessageFormatter, memberNames) { } } } ================================================ FILE: src/Nancy/Validation/Rules/RegexValidationRule.cs ================================================ namespace Nancy.Validation.Rules { using System; using System.Collections.Generic; /// /// Implementation of for ensuring a string matches the /// pattern which is defined by a regex. /// public class RegexValidationRule : ModelValidationRule { /// /// Initializes a new instance of the class. /// /// The error message formatter. /// The member names. /// The regex pattern that should be used to check for a match. public RegexValidationRule(Func errorMessageFormatter, IEnumerable memberNames, string pattern) : base("Regex", errorMessageFormatter, memberNames) { this.Pattern = pattern; } /// /// The regex pattern that should be used to check for a match. /// public string Pattern { get; private set; } } } ================================================ FILE: src/Nancy/Validation/Rules/StringLengthValidationRule.cs ================================================ namespace Nancy.Validation.Rules { using System; using System.Collections.Generic; /// /// Implementation of for ensuring that the length of a string /// is withing the specified range. /// public class StringLengthValidationRule : ModelValidationRule { /// /// Initializes a new instance of the class. /// /// The error message formatter. /// The member names. /// Minimum allowed length of the string /// Maximum allowed length of the string public StringLengthValidationRule(Func errorMessageFormatter, IEnumerable memberNames, int minLength, int maxLength) : base("StringLength", errorMessageFormatter, memberNames) { this.MinLength = minLength; this.MaxLength = maxLength; } /// /// Gets the length of the min. /// /// The length of the min. public int MinLength { get; private set; } /// /// Gets the length of the max. /// /// The length of the max. public int MaxLength { get; private set; } } } ================================================ FILE: src/Nancy/ViewConfiguration.cs ================================================ namespace Nancy { /// /// Configuration for view rendering. /// public class ViewConfiguration { /// /// A default instance of the class. /// public static readonly ViewConfiguration Default = new ViewConfiguration( runtimeViewDiscovery: false, runtimeViewUpdates: false); private ViewConfiguration() { } /// /// Initializes a new instance of the class. /// /// Determines if views can be discovered during runtime. /// Determines if views can be updated during runtime. public ViewConfiguration(bool runtimeViewDiscovery = false, bool runtimeViewUpdates = false) { this.RuntimeViewDiscovery = runtimeViewDiscovery; this.RuntimeViewUpdates = runtimeViewUpdates; } /// /// Gets or sets a value indicating whether or not to enable runtime view discovery /// /// if views can be discovered during runtime, otherwise . public bool RuntimeViewDiscovery { get; private set; } /// /// Gets a value indicating whether or not to allow runtime changes of views. /// /// if views can be updated during runtime, otherwise . public bool RuntimeViewUpdates { get; private set; } } } ================================================ FILE: src/Nancy/ViewConfigurationExtensions.cs ================================================ namespace Nancy { using Configuration; /// /// Contains configuration extensions for . /// public static class ViewConfigurationExtensions { /// /// Configures . /// /// An that should be configured. /// if views can be discovered during runtime, otherwise . /// if views can be updated during runtime, otherwise . public static void Views(this INancyEnvironment environment, bool? runtimeViewDiscovery = false, bool? runtimeViewUpdates = false) { environment.AddValue(new ViewConfiguration( runtimeViewDiscovery: runtimeViewDiscovery ?? ViewConfiguration.Default.RuntimeViewDiscovery, runtimeViewUpdates: runtimeViewUpdates ?? ViewConfiguration.Default.RuntimeViewUpdates)); } } } ================================================ FILE: src/Nancy/ViewEngines/AmbiguousViewsException.cs ================================================ namespace Nancy.ViewEngines { using System; /// /// Thrown when multiple instances describe the exact same view. /// public class AmbiguousViewsException : Exception { /// /// Initializes a new instance of the class. /// public AmbiguousViewsException() { } /// /// Initializes a new instance of the class. /// /// The message that should be displayed with the exception. public AmbiguousViewsException(string message) : base(message) { } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultFileSystemReader.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; using System.Linq; /// /// Default implementation for retrieving information about views that are stored on the file system. /// public class DefaultFileSystemReader : IFileSystemReader { /// /// Gets information about view that are stored in folders below the applications root path. /// /// The path of the folder where the views should be looked for. /// A list of view extensions to look for. /// An containing view locations and contents readers. public IEnumerable>> GetViewsWithSupportedExtensions(string path, IEnumerable supportedViewExtensions) { return supportedViewExtensions .SelectMany(extension => GetFilenames(path, extension)) .Distinct() .Select(file => new Tuple>(file, () => new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))) .ToList(); } /// /// Gets the last modified time for the file specified /// /// Filename /// Time the file was last modified public DateTime GetLastModified(string filename) { return File.GetLastWriteTimeUtc(filename); } /// /// Gets information about specific views that are stored in folders below the applications root path. /// /// The path of the folder where the views should be looked for. /// Name of the view to search for /// A list of view extensions to look for. /// An containing view locations and contents readers. public IEnumerable>> GetViewsWithSupportedExtensions(string path, string viewName, IEnumerable supportedViewExtensions) { return GetFilenames(path, viewName, supportedViewExtensions) .Select(file => new Tuple>(file, () => new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))) .ToList(); } private static IEnumerable GetFilenames(string path, string viewName, IEnumerable supportedViewExtensions) { return Directory.GetFiles(path, viewName + ".*", SearchOption.TopDirectoryOnly) .Where(f => IsValidExtention(f, supportedViewExtensions)); } private static bool IsValidExtention(string filename, IEnumerable supportedViewExtensions) { var extension = Path.GetExtension(filename); if (string.IsNullOrEmpty(extension)) { return false; } return supportedViewExtensions.Contains(extension.Substring(1)); } private static IEnumerable GetFilenames(string path, string extension) { return !Directory.Exists(path) ? Enumerable.Empty() : Directory.GetFiles(path, string.Concat("*.", extension), SearchOption.AllDirectories); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultRenderContext.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using Nancy.Extensions; using Nancy.Helpers; using Nancy.Localization; using Nancy.Security; /// /// Default render context implementation. /// public class DefaultRenderContext : IRenderContext { private readonly IViewResolver viewResolver; private readonly IViewCache viewCache; private readonly ITextResource textResource; private readonly ViewLocationContext viewLocationContext; private TextResourceFinder textResourceFinder; /// /// Initializes a new instance of the class. /// /// /// /// /// public DefaultRenderContext(IViewResolver viewResolver, IViewCache viewCache, ITextResource textResource, ViewLocationContext viewLocationContext) { this.viewResolver = viewResolver; this.viewCache = viewCache; this.textResource = textResource; this.viewLocationContext = viewLocationContext; this.textResourceFinder = new TextResourceFinder(textResource, viewLocationContext.Context); } /// /// Gets the context of the current request. /// /// A instance. public NancyContext Context { get { return this.viewLocationContext.Context; } } /// /// Gets the view cache that is used by Nancy. /// /// An instance. public IViewCache ViewCache { get { return this.viewCache; } } /// /// Gets the text resource for localisation /// public ITextResource TextResource { get { return this.textResource; } } /// /// Gets the text resource finder for localisation /// public dynamic TextResourceFinder { get { return this.textResourceFinder; } } /// /// Parses a path and returns an absolute url path, taking into account /// base directory etc. /// /// Input url such as ~/styles/main.css /// Parsed absolute url path public string ParsePath(string input) { return this.viewLocationContext.Context.ToFullPath(input); } /// /// HTML encodes a string. /// /// The string that should be HTML encoded. /// A HTML encoded . public string HtmlEncode(string input) { return HttpUtility.HtmlEncode(input); } /// /// Locates a view that matches the provided and . /// /// The name of the view that should be located. /// The model that should be used when locating the view. /// A instance if the view could be located; otherwise, . public ViewLocationResult LocateView(string viewName, dynamic model) { return this.viewResolver.GetViewLocation(viewName, model, this.viewLocationContext); } /// /// Generates a Csrf token. /// The token should be stored in a cookie and the form as a hidden field. /// In both cases the name should be the key of the returned key value pair. /// /// A tuple containing the name (cookie name and form/querystring name) and value public KeyValuePair GetCsrfToken() { object tokenObject; if (!this.viewLocationContext.Context.Items.TryGetValue(CsrfToken.DEFAULT_CSRF_KEY, out tokenObject)) { throw new InvalidOperationException("CSRF is not enabled on this request"); } var tokenString = tokenObject as string; if (string.IsNullOrEmpty(tokenString)) { throw new InvalidOperationException("CSRF object is invalid"); } return new KeyValuePair(CsrfToken.DEFAULT_CSRF_KEY, tokenString); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultRenderContextFactory.cs ================================================ namespace Nancy.ViewEngines { using Nancy.Localization; /// /// Default render context factory implementation. /// public class DefaultRenderContextFactory : IRenderContextFactory { private readonly IViewCache viewCache; private readonly IViewResolver viewResolver; private readonly ITextResource textResource; /// /// Initializes a new instance of the class. /// /// The view cache that should be used by the created render context. /// The view resolver that should be used by the created render context. /// The that should be used by the engine. public DefaultRenderContextFactory(IViewCache viewCache, IViewResolver viewResolver, ITextResource textResource) { this.viewCache = viewCache; this.viewResolver = viewResolver; this.textResource = textResource; } /// /// Gets a for the specified . /// /// The for which the context should be created. /// A instance. public IRenderContext GetRenderContext(ViewLocationContext viewLocationContext) { return new DefaultRenderContext(this.viewResolver, this.viewCache, this.textResource, viewLocationContext); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultResourceReader.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; /// /// Default implementation for extracting view information form an assembly. /// public class DefaultResourceReader : IResourceReader { /// /// Gets information about the resources that are embedded in the assembly. /// /// The to retrieve view information from. /// A list of view extensions to look for. /// A of resource locations and content readers. public IList>> GetResourceStreamMatches(Assembly assembly, IEnumerable supportedViewEngineExtensions) { var resourceStreams = from resourceName in assembly.GetManifestResourceNames() from viewEngineExtension in supportedViewEngineExtensions where GetResourceExtension(resourceName).Equals(viewEngineExtension, StringComparison.OrdinalIgnoreCase) select new Tuple>( resourceName, () => new StreamReader(assembly.GetManifestResourceStream(resourceName))); return resourceStreams.ToList(); } private static string GetResourceExtension(string resourceName) { var extension = Path.GetExtension(resourceName); return string.IsNullOrEmpty(extension) ? string.Empty : extension.Substring(1); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultViewCache.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Concurrent; using Configuration; /// /// Default implementation of . /// /// Supports expiring content if it is stale, through the setting. public class DefaultViewCache : IViewCache { private readonly ConcurrentDictionary cache; private readonly ViewConfiguration configuration; /// /// Initializes a new instance of the class. /// public DefaultViewCache(INancyEnvironment environment) { this.cache = new ConcurrentDictionary(); this.configuration = environment.GetValue(); } /// /// Gets or adds a view from the cache. /// /// The type of the cached view instance. /// A instance that describes the view that is being added or retrieved from the cache. /// A function that produces the value that should be added to the cache in case it does not already exist. /// An instance of the type specified by the type. public TCompiledView GetOrAdd(ViewLocationResult viewLocationResult, Func valueFactory) { if (this.configuration.RuntimeViewUpdates) { if (viewLocationResult.IsStale()) { object old; this.cache.TryRemove(viewLocationResult, out old); } } return (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x)); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultViewFactory.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.Dynamic; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Nancy.Conventions; /// /// The default implementation for how views are resolved and rendered by Nancy. /// public class DefaultViewFactory : IViewFactory { private readonly IViewResolver viewResolver; private readonly IEnumerable viewEngines; private readonly IRenderContextFactory renderContextFactory; private readonly ViewLocationConventions conventions; private readonly IRootPathProvider rootPathProvider; private static readonly Action EmptyView = x => { }; private readonly string[] viewEngineExtensions; /// /// Initializes a new instance of the class. /// /// An instance that should be used to resolve the location of a view. /// An instance containing the instances that should be able to be used to render a view /// A instance that should be used to create an when a view is rendered. /// An instance that should be used to resolve all possible view locations /// An instance. public DefaultViewFactory(IViewResolver viewResolver, IEnumerable viewEngines, IRenderContextFactory renderContextFactory, ViewLocationConventions conventions, IRootPathProvider rootPathProvider) { this.viewResolver = viewResolver; this.viewEngines = viewEngines; this.renderContextFactory = renderContextFactory; this.conventions = conventions; this.rootPathProvider = rootPathProvider; this.viewEngineExtensions = this.viewEngines.SelectMany(ive => ive.Extensions).ToArray(); } /// /// Renders the view with the name and model defined by the and parameters. /// /// The name of the view to render. /// The model that should be passed into the view. /// A instance, containing information about the context for which the view is being rendered. /// A delegate that can be invoked with the that the view should be rendered to. public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext) { if (viewName == null && model == null) { throw new ArgumentException("View name and model parameters cannot both be null."); } if (model == null && viewName.Length == 0) { throw new ArgumentException("The view name parameter cannot be empty when the model parameters is null."); } if (viewLocationContext == null) { throw new ArgumentNullException("viewLocationContext", "The value of the viewLocationContext parameter cannot be null."); } var actualViewName = viewName ?? GetViewNameFromModel(model, viewLocationContext.Context); viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewFactory] Rendering view with name ", actualViewName))); return this.GetRenderedView(actualViewName, model, viewLocationContext); } private Response GetRenderedView(string viewName, dynamic model, ViewLocationContext viewLocationContext) { var viewLocationResult = this.viewResolver.GetViewLocation(viewName, model, viewLocationContext); var resolvedViewEngine = GetViewEngine(viewLocationResult, viewLocationContext.Context); if (resolvedViewEngine == null) { viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine("[DefaultViewFactory] Unable to find view engine that could render the view.")); throw new ViewNotFoundException(viewName, this.viewEngineExtensions, this.GetInspectedLocations(viewName, model, viewLocationContext), this.rootPathProvider); } viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewFactory] Rendering view with view engine ", resolvedViewEngine.GetType().FullName))); return SafeInvokeViewEngine( resolvedViewEngine, viewLocationResult, GetSafeModel(model), this.renderContextFactory.GetRenderContext(viewLocationContext) ); } private string[] GetInspectedLocations(string viewName, dynamic model, ViewLocationContext viewLocationContext) { var inspectedLocations = new List(); foreach (var convention in this.conventions) { try { var location = convention.Invoke(viewName, model, viewLocationContext); if (!string.IsNullOrWhiteSpace(location)) { inspectedLocations.Add(location); } } catch { // ignored } } return inspectedLocations.ToArray(); } private static object GetSafeModel(object model) { return (model.IsAnonymousType()) ? GetExpandoObject(model) : model; } private static ExpandoObject GetExpandoObject(object source) { var expandoObject = new ExpandoObject(); IDictionary results = expandoObject; foreach (var propertyInfo in source.GetType().GetProperties()) { results[propertyInfo.Name] = propertyInfo.GetValue(source, null); } return expandoObject; } private IViewEngine GetViewEngine(ViewLocationResult viewLocationResult, NancyContext context) { if (viewLocationResult == null) { return null; } context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewFactory] Attempting to resolve view engine for view extension ", viewLocationResult.Extension))); var matchingViewEngines = from viewEngine in this.viewEngines where viewEngine.Extensions.Any(x => x.Equals(viewLocationResult.Extension, StringComparison.OrdinalIgnoreCase)) select viewEngine; return matchingViewEngines.FirstOrDefault(); } private static string GetViewNameFromModel(dynamic model, NancyContext context) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewFactory] Extracting view name from model of type ", model.GetType().FullName))); return Regex.Replace(model.GetType().Name, "Model$", string.Empty); } private static Response SafeInvokeViewEngine(IViewEngine viewEngine, ViewLocationResult locationResult, dynamic model, IRenderContext renderContext) { try { return viewEngine.RenderView(locationResult, model, renderContext); } catch (Exception) { return EmptyView; } } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultViewLocator.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using Configuration; /// /// The default implementation of . /// public class DefaultViewLocator : IViewLocator { private readonly List viewLocationResults; private readonly IViewLocationProvider viewLocationProvider; private readonly IEnumerable viewEngines; private readonly ReaderWriterLockSlim padlock = new ReaderWriterLockSlim(); private readonly char[] invalidCharacters; private readonly ViewConfiguration configuration; /// /// Initializes a new instance of the class. /// /// An instance. /// An of instances. /// An instance. public DefaultViewLocator(IViewLocationProvider viewLocationProvider, IEnumerable viewEngines, INancyEnvironment environment) { this.viewLocationProvider = viewLocationProvider; this.viewEngines = viewEngines; this.invalidCharacters = Path.GetInvalidFileNameChars().Where(c => c != '/').ToArray(); this.viewLocationResults = new List(this.GetInitialViewLocations()); this.configuration = environment.GetValue(); } /// /// Gets the location of the view defined by the parameter. /// /// Name of the view to locate. /// The instance for the current request. /// A instance if the view could be located; otherwise . public ViewLocationResult LocateView(string viewName, NancyContext context) { if (string.IsNullOrEmpty(viewName)) { return null; } if (!this.IsValidViewName(viewName)) { return null; } // If we can't do runtime discovery there's no need to lock anything // as we can assume our cache is immutable. if (!this.configuration.RuntimeViewDiscovery) { return this.LocateCachedView(viewName); } this.padlock.EnterUpgradeableReadLock(); try { var cachedResult = this.LocateCachedView(viewName); if (cachedResult != null) { return cachedResult; } return !this.configuration.RuntimeViewDiscovery ? null : this.LocateAndCacheUncachedView(viewName); } finally { this.padlock.ExitUpgradeableReadLock(); } } /// /// Gets all the views that are currently discovered /// Note: this is *not* the recommended way to deal with the view locator /// as it doesn't allow for runtime discovery of views with the /// . /// /// A collection of instances public IEnumerable GetAllCurrentlyDiscoveredViews() { this.padlock.EnterReadLock(); try { // Make a copy to avoid any modification issues var newList = new List(this.viewLocationResults.Count); this.viewLocationResults.ForEach(newList.Add); return newList; } finally { this.padlock.ExitReadLock(); } } private ViewLocationResult LocateAndCacheUncachedView(string viewName) { var uncachedResults = this.GetUncachedMatchingViews(viewName); if (!uncachedResults.Any()) { return null; } this.padlock.EnterWriteLock(); try { this.viewLocationResults.AddRange(uncachedResults); } finally { this.padlock.ExitWriteLock(); } if (uncachedResults.Length > 1) { throw new AmbiguousViewsException(GetAmbiguousViewExceptionMessage(uncachedResults.Length, uncachedResults)); } return uncachedResults.First(); } private ViewLocationResult LocateCachedView(string viewName) { var cachedResults = this.GetCachedMatchingViews(viewName); if (cachedResults.Length == 1) { return cachedResults.Single(); } if (cachedResults.Length > 1) { throw new AmbiguousViewsException(GetAmbiguousViewExceptionMessage(cachedResults.Length, cachedResults)); } return null; } private ViewLocationResult[] GetUncachedMatchingViews(string viewName) { var viewExtension = GetExtensionFromViewName(viewName); var supportedViewExtensions = string.IsNullOrEmpty(viewExtension) ? this.GetSupportedViewExtensions() : new[] { viewExtension }; var location = GetLocationFromViewName(viewName); var nameWithoutExtension = GetFilenameWithoutExtensionFromViewName(viewName); return this.viewLocationProvider.GetLocatedViews(supportedViewExtensions, location, nameWithoutExtension) .ToArray(); } private ViewLocationResult[] GetCachedMatchingViews(string viewName) { return this.viewLocationResults.Where(x => NameMatchesView(viewName, x)) .Where(x => ExtensionMatchesView(viewName, x)) .Where(x => LocationMatchesView(viewName, x)) .ToArray(); } private IEnumerable GetInitialViewLocations() { var supportedViewExtensions = this.GetSupportedViewExtensions(); var viewsLocatedByProviders = this.viewLocationProvider.GetLocatedViews(supportedViewExtensions); return viewsLocatedByProviders.ToArray(); } private IEnumerable GetSupportedViewExtensions() { return this.viewEngines .SelectMany(engine => engine.Extensions) .Distinct(); } private static string GetAmbiguousViewExceptionMessage(int count, IEnumerable viewsThatMatchesCriteria) { return string.Format("This exception was thrown because multiple views were found. {0} view(s):\r\n\t{1}", count, string.Join("\r\n\t", viewsThatMatchesCriteria.Select(GetFullLocationOfView).ToArray())); } private static string GetFullLocationOfView(ViewLocationResult viewLocationResult) { return string.Concat(viewLocationResult.Location, "/", viewLocationResult.Name, ".", viewLocationResult.Extension); } private static bool ExtensionMatchesView(string viewName, ViewLocationResult viewLocationResult) { var extension = GetExtensionFromViewName(viewName); return string.IsNullOrEmpty(extension) || viewLocationResult.Extension.Equals(extension, StringComparison.OrdinalIgnoreCase); } private static bool LocationMatchesView(string viewName, ViewLocationResult viewLocationResult) { var location = GetLocationFromViewName(viewName); return viewLocationResult.Location.Equals(location, StringComparison.OrdinalIgnoreCase); } private static bool NameMatchesView(string viewName, ViewLocationResult viewLocationResult) { var name = GetFilenameWithoutExtensionFromViewName(viewName); return (!string.IsNullOrEmpty(name)) && viewLocationResult.Name.Equals(name, StringComparison.OrdinalIgnoreCase); } private static string GetFilenameWithoutExtensionFromViewName(string viewName) { return Path.GetFileNameWithoutExtension(viewName); } private static string GetLocationFromViewName(string viewName) { var filename = Path.GetFileName(viewName); var index = viewName.LastIndexOf(filename, StringComparison.OrdinalIgnoreCase); var location = index >= 0 ? viewName.Remove(index, filename.Length) : viewName; location = location.TrimEnd('/'); return location; } private static string GetExtensionFromViewName(string viewName) { var extension = Path.GetExtension(viewName); return !string.IsNullOrEmpty(extension) ? extension.Substring(1) : extension; } private bool IsValidViewName(string viewName) { return !this.invalidCharacters.Any(viewName.Contains); } } } ================================================ FILE: src/Nancy/ViewEngines/DefaultViewResolver.cs ================================================ namespace Nancy.ViewEngines { using System; using Nancy.Conventions; /// /// Default implementation on how views are resolved by Nancy. /// public class DefaultViewResolver : IViewResolver { private readonly ViewLocationConventions conventions; private readonly IViewLocator viewLocator; /// /// Initializes a new instance of the class. /// /// The view locator that should be used to locate views. /// The conventions that the view resolver should use to figure out where to look for views. public DefaultViewResolver(IViewLocator viewLocator, ViewLocationConventions conventions) { if (viewLocator == null) { throw new InvalidOperationException("Cannot create an instance of DefaultViewResolver with view locator parameter having null value."); } if (conventions == null) { throw new InvalidOperationException("Cannot create an instance of DefaultViewResolver with conventions parameter having null value."); } this.viewLocator = viewLocator; this.conventions = conventions; } /// /// Locates a view based on the provided information. /// /// The name of the view to locate. /// The model that will be used with the view. /// A instance, containing information about the context for which the view is being located. /// A instance if the view could be found, otherwise . public ViewLocationResult GetViewLocation(string viewName, dynamic model, ViewLocationContext viewLocationContext) { if (string.IsNullOrEmpty(viewName)) { return null; } if (viewLocationContext == null) { return null; } viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewResolver] Resolving view for '", viewName , "', using view location conventions."))); foreach (var convention in conventions) { var conventionBasedViewName = SafeInvokeConvention(convention, viewName, model, viewLocationContext); if (string.IsNullOrEmpty(conventionBasedViewName)) { continue; } viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewResolver] Attempting to locate view using convention '", conventionBasedViewName, "'"))); var locatedView = this.viewLocator.LocateView(conventionBasedViewName, viewLocationContext.Context); if (locatedView != null) { viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[DefaultViewResolver] View resolved at '", conventionBasedViewName, "'"))); return locatedView; } } viewLocationContext.Context.Trace.TraceLog.WriteLog(x => x.AppendLine("[DefaultViewResolver] No view could be resolved using the available view location conventions.")); return null; } private static string SafeInvokeConvention(Func convention, string viewName, dynamic model, ViewLocationContext viewLocationContext) { try { return convention.Invoke(viewName, model, viewLocationContext); } catch { return null; } } } } ================================================ FILE: src/Nancy/ViewEngines/Extensions.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; /// /// Contains miscellaneous extension methods. /// public static class Extensions { /// /// Checks if the evaluated instance is an anonymous /// /// The object instance to check. /// if the object is an anonymous type; otherwise . public static bool IsAnonymousType(this object source) { return source != null && source.GetType().IsAnonymousType(); } /// /// Determines whether the given type is anonymous or not. /// /// The type. /// if type is anonymous, otherwise public static bool IsAnonymousType(this Type type) { if (type == null) { return false; } return type.GetTypeInfo().IsGenericType && (type.GetTypeInfo().Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic && (type.Name.StartsWith("<>", StringComparison.OrdinalIgnoreCase) || type.Name.StartsWith("VB$", StringComparison.OrdinalIgnoreCase)) && (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType")) && type.GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute)).Any(); } } } ================================================ FILE: src/Nancy/ViewEngines/FileSystemViewLocationProvider.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; using System.IO; using System.Linq; /// /// Contains the functionality for locating a view that is located on the file system. /// public class FileSystemViewLocationProvider : IViewLocationProvider { private readonly IFileSystemReader fileSystemReader; private readonly string rootPath; /// /// Initializes a new instance of the class, with /// the provided . /// /// A instance. /// Creating an instance using this constructor will result in the being used internally. public FileSystemViewLocationProvider(IRootPathProvider rootPathProvider) : this(rootPathProvider, new DefaultFileSystemReader()) { } /// /// Initializes a new instance of the class, with /// the provided and . /// /// A instance. /// An instance that should be used when retrieving view information from the file system. public FileSystemViewLocationProvider(IRootPathProvider rootPathProvider, IFileSystemReader fileSystemReader) { this.fileSystemReader = fileSystemReader; this.rootPath = rootPathProvider.GetRootPath(); } /// /// Returns an instance for all the views that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . public IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions) { if (string.IsNullOrEmpty(this.rootPath)) { return Enumerable.Empty(); } return this.GetViewsFromPath(this.rootPath, supportedViewExtensions); } /// /// Returns an instance for all the views matching the viewName that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// Location of the view /// The name of the view to try and find /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . public IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions, string location, string viewName) { if (string.IsNullOrEmpty(this.rootPath)) { return Enumerable.Empty(); } var path = this.rootPath; if (!string.IsNullOrEmpty(location)) { path = Path.Combine(path, location.Replace('/', Path.DirectorySeparatorChar)); } if (!Directory.Exists(path)) { return Enumerable.Empty(); } var results = this.GetViewsFromPath(path, viewName, supportedViewExtensions); return results; } private IEnumerable GetViewsFromPath(string path, string viewName, IEnumerable supportedViewExtensions) { var matches = this.fileSystemReader.GetViewsWithSupportedExtensions(path, viewName, supportedViewExtensions); return from match in matches select new FileSystemViewLocationResult( GetViewLocation(match.Item1, this.rootPath), Path.GetFileNameWithoutExtension(match.Item1), Path.GetExtension(match.Item1).Substring(1), match.Item2, match.Item1, this.fileSystemReader); } private IEnumerable GetViewsFromPath(string path, IEnumerable supportedViewExtensions) { var matches = this.fileSystemReader.GetViewsWithSupportedExtensions(path, supportedViewExtensions); return from match in matches select new FileSystemViewLocationResult( GetViewLocation(match.Item1, this.rootPath), Path.GetFileNameWithoutExtension(match.Item1), Path.GetExtension(match.Item1).Substring(1), match.Item2, match.Item1, this.fileSystemReader); } private static string GetViewLocation(string match, string rootPath) { var location = match .Replace(rootPath, string.Empty) .TrimStart(new[] { Path.DirectorySeparatorChar }) .Replace(@"\", "/") .Replace(Path.GetFileName(match), string.Empty) .TrimEnd(new [] { '/' }); return location; } } } ================================================ FILE: src/Nancy/ViewEngines/FileSystemViewLocationResult.cs ================================================ namespace Nancy.ViewEngines { using System; using System.IO; /// /// View location result for file system based views. /// Supports detecting if the contents have changed since it /// was last read. /// public class FileSystemViewLocationResult : ViewLocationResult { private readonly IFileSystemReader fileSystem; private readonly string fileName; private DateTime lastUpdated; private readonly Func fileContents; /// /// Initializes a new instance of the class. /// /// The location of where the view was found. /// The name of the view. /// The file extension of the located view. /// A that can be used to read the contents of the located view. /// Full filename of the file /// An instance that should be used when retrieving view information from the file system. public FileSystemViewLocationResult(string location, string name, string extension, Func contents, string fullFilename, IFileSystemReader fileSystem) { this.fileSystem = fileSystem; this.Location = location; this.Name = name; this.Extension = extension; this.fileContents = contents; this.Contents = this.GetContents; this.fileName = fullFilename; } /// /// Gets a value indicating whether the current item is stale /// /// True if stale, false otherwise public override bool IsStale() { return this.lastUpdated != this.fileSystem.GetLastModified(this.fileName); } /// /// Wraps the real contents delegate to set the last modified date first /// /// TextReader to read the file private TextReader GetContents() { this.lastUpdated = this.fileSystem.GetLastModified(this.fileName); return this.fileContents.Invoke(); } } } ================================================ FILE: src/Nancy/ViewEngines/IFileSystemReader.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; /// /// Defines the functionality for retrieving information about views that are stored on the file system. /// public interface IFileSystemReader { /// /// Gets information about view that are stored in folders below the applications root path. /// /// The path of the folder where the views should be looked for. /// A list of view extensions to look for. /// An containing view locations and contents readers. IEnumerable>> GetViewsWithSupportedExtensions(string path, IEnumerable supportedViewExtensions); /// /// Gets the last modified time for the file specified /// /// Filename /// Time the file was last modified DateTime GetLastModified(string filename); /// /// Gets information about specific views that are stored in folders below the applications root path. /// /// The path of the folder where the views should be looked for. /// Name of the view to search for /// A list of view extensions to look for. /// An containing view locations and contents readers. IEnumerable>> GetViewsWithSupportedExtensions(string path, string viewName, IEnumerable supportedViewExtensions); } } ================================================ FILE: src/Nancy/ViewEngines/IRenderContext.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; using Nancy.Localization; /// /// Defines the functionality of the context that is passed into a view engine when the view is requested to be rendered. /// public interface IRenderContext { /// /// Gets the context of the current request. /// /// A instance. NancyContext Context { get; } /// /// Gets the view cache that is used by Nancy. /// /// An instance. IViewCache ViewCache { get; } /// /// Gets the text resource for localisation /// ITextResource TextResource { get; } /// /// Gets the text resource finder for localisation /// dynamic TextResourceFinder { get; } /// /// Parses a path and returns an absolute url path, taking into account /// base directory etc. /// /// Input url such as ~/styles/main.css /// Parsed absolute url path string ParsePath(string input); /// /// HTML encodes a string. /// /// The string that should be HTML encoded. /// A HTML encoded . string HtmlEncode(string input); /// /// Locates a view that matches the provided and . /// /// The name of the view that should be located. /// The model that should be used when locating the view. /// A instance if the view could be located; otherwise, . ViewLocationResult LocateView(string viewName, dynamic model); /// /// Gets the current Csrf token. /// The token should be stored in a cookie and the form as a hidden field. /// In both cases the name should be the key of the returned key value pair. /// /// A tuple containing the name (cookie name and form/querystring name) and value KeyValuePair GetCsrfToken(); } } ================================================ FILE: src/Nancy/ViewEngines/IRenderContextFactory.cs ================================================ namespace Nancy.ViewEngines { /// /// Defines the functionality required to manufacture instances. /// public interface IRenderContextFactory { /// /// Gets a for the specified . /// /// The for which the context should be created. /// A instance. IRenderContext GetRenderContext(ViewLocationContext viewLocationContext); } } ================================================ FILE: src/Nancy/ViewEngines/IResourceReader.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; using System.Reflection; /// /// Defines the functionality of a reader that extracts embedded views from an assembly. /// public interface IResourceReader { /// /// Gets information about the resources that are embedded in the assembly. /// /// The to retrieve view information from. /// A list of view extensions to look for. /// A of resource locations and content readers. IList>> GetResourceStreamMatches(Assembly assembly, IEnumerable supportedViewEngineExtensions); } } ================================================ FILE: src/Nancy/ViewEngines/IViewCache.cs ================================================ namespace Nancy.ViewEngines { using System; /// /// Defines the functionality of a Nancy view cache. /// public interface IViewCache { /// /// Gets or adds a view from the cache. /// /// The type of the cached view instance. /// A instance that describes the view that is being added or retrieved from the cache. /// A function that produces the value that should be added to the cache in case it does not already exist. /// An instance of the type specified by the type. TCompiledView GetOrAdd(ViewLocationResult viewLocationResult, Func valueFactory); } } ================================================ FILE: src/Nancy/ViewEngines/IViewEngine.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; /// /// Defines the functionality that a view engine must support to be integrated into Nancy. /// public interface IViewEngine { /// /// Gets the extensions file extensions that are supported by the view engine. /// /// An instance containing the extensions. /// The extensions should not have a leading dot in the name. IEnumerable Extensions { get; } /// /// Initialise the view engine (if necessary) /// /// Startup context void Initialize(ViewEngineStartupContext viewEngineStartupContext); /// /// Renders the view. /// /// A instance, containing information on how to get the view template. /// The model that should be passed into the view /// /// A response Response RenderView(ViewLocationResult viewLocationResult, dynamic model, IRenderContext renderContext); } } ================================================ FILE: src/Nancy/ViewEngines/IViewFactory.cs ================================================ namespace Nancy.ViewEngines { /// /// Defines the functionality used by a to render a view to the response. /// public interface IViewFactory : IHideObjectMembers { /// /// Renders the view with the name and model defined by the and parameters. /// /// The name of the view to render. /// The module path of the module that is rendering the view. /// A instance, containing information about the context for which the view is being rendered. /// A response. Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext); } } ================================================ FILE: src/Nancy/ViewEngines/IViewLocationProvider.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; /// /// Defines the functionality used by Nancy to located a view. /// public interface IViewLocationProvider { /// /// Returns an instance for all the views that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions); /// /// Returns an instance for all the views matching the viewName that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// Location of the view /// The name of the view to try and find /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions, string location, string viewName); } } ================================================ FILE: src/Nancy/ViewEngines/IViewLocator.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; /// /// Defines the functionality for locating the requested view. /// public interface IViewLocator : IHideObjectMembers { /// /// Gets the location of the view defined by the parameter. /// /// Name of the view to locate. /// The instance for the current request. /// A instance if the view could be located; otherwise . ViewLocationResult LocateView(string viewName, NancyContext context); /// /// Gets all the views that are currently discovered /// Note: this is *not* the recommended way to deal with the view locator /// as it doesn't allow for runtime discovery of views with the /// . /// /// A collection of instances IEnumerable GetAllCurrentlyDiscoveredViews(); } } ================================================ FILE: src/Nancy/ViewEngines/IViewResolver.cs ================================================ namespace Nancy.ViewEngines { /// /// Defines the functionality for resolving the requested view. /// public interface IViewResolver : IHideObjectMembers { /// /// Locates a view based on the provided information. /// /// The name of the view to locate. /// The model that will be used with the view. /// A instance, containing information about the context for which the view is being located. /// A instance if the view could be found, otherwise . ViewLocationResult GetViewLocation(string viewName, dynamic model, ViewLocationContext viewLocationContext); } } ================================================ FILE: src/Nancy/ViewEngines/ResourceViewLocationProvider.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; /// /// Contains the functionality for locating a view that has been embedded into an assembly resource. /// public class ResourceViewLocationProvider : IViewLocationProvider { private readonly IResourceReader resourceReader; private readonly IResourceAssemblyProvider resourceAssemblyProvider; /// /// User-configured root namespaces for assemblies. /// public static readonly IDictionary RootNamespaces = new Dictionary(); /// /// A list of assemblies to ignore when scanning for embedded views. /// public static readonly IList Ignore = new List(); /// /// Initializes a new instance of the class, with /// the provided . /// /// An instance. public ResourceViewLocationProvider(IAssemblyCatalog assemblyCatalog) : this(new DefaultResourceReader(), new ResourceAssemblyProvider(assemblyCatalog)) { } /// /// Initializes a new instance of the class, with /// the provided and . /// /// An instance that should be used when extracting embedded views. /// An instance that should be used to determine which assemblies to scan for embedded views. public ResourceViewLocationProvider(IResourceReader resourceReader, IResourceAssemblyProvider resourceAssemblyProvider) { this.resourceReader = resourceReader; this.resourceAssemblyProvider = resourceAssemblyProvider; } /// /// Returns an instance for all the views that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . public IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions) { if (supportedViewExtensions == null || !supportedViewExtensions.Any()) { return Enumerable.Empty(); } return this.resourceAssemblyProvider .GetAssembliesToScan() .Union(RootNamespaces.Keys) .Where(x => !Ignore.Contains(x)) .SelectMany(x => GetViewLocations(x, supportedViewExtensions)); } /// /// Returns an instance for all the views matching the viewName that could be located by the provider. /// /// An instance, containing the view engine file extensions that is supported by the running instance of Nancy. /// Location of the view /// The name of the view to try and find /// An instance, containing instances for the located views. /// If no views could be located, this method should return an empty enumerable, never . public IEnumerable GetLocatedViews(IEnumerable supportedViewExtensions, string location, string viewName) { var allResults = this.GetLocatedViews(supportedViewExtensions); return allResults.Where(vlr => vlr.Location.Equals(location, StringComparison.OrdinalIgnoreCase) && vlr.Name.Equals(viewName, StringComparison.OrdinalIgnoreCase)); } private IEnumerable GetViewLocations(Assembly assembly, IEnumerable supportedViewExtensions) { var resourceStreams = this.resourceReader.GetResourceStreamMatches(assembly, supportedViewExtensions); if (!resourceStreams.Any()) { return Enumerable.Empty(); } if (resourceStreams.Count() == 1 && !RootNamespaces.ContainsKey(assembly)) { var errorMessage = string.Format("Only one view was found in assembly {0}, but no rootnamespace had been registered.", assembly.FullName); throw new InvalidOperationException(errorMessage); } string commonNamespace; if (!RootNamespaces.TryGetValue(assembly, out commonNamespace)) { commonNamespace = ExtractAssemblyRootNamespace(assembly); } if (string.IsNullOrWhiteSpace(commonNamespace)) { return Enumerable.Empty(); } return from resource in resourceStreams let resourceFileName = GetResourceFileName(resource.Item1) where !resourceFileName.Equals(string.Empty) select new ViewLocationResult( GetResourceLocation(commonNamespace, resource.Item1, resourceFileName), Path.GetFileNameWithoutExtension(resourceFileName), GetResourceExtension(resource.Item1), resource.Item2); } private static string GetResourceLocation(string commonNamespace, string resource, string resourceName) { return resource .Replace(commonNamespace, string.Empty) .Replace(resourceName, string.Empty) .Trim(new[] { '.' }) .Replace(".", "/"); } private static string ExtractCommonResourceNamespace(IEnumerable resources) { if (resources.Count() == 1) { var resource = resources.First(); return resource .Replace(GetResourceFileName(resource), string.Empty) .TrimEnd(new[] { '.' }); } var commonPathSegments = resources.Select(s => new { parts = s.Split('.') }) .Aggregate((previous, current) => new { parts = current.parts.TakeWhile((step, index) => step == previous.parts.ElementAtOrDefault(index)).ToArray() }); var commonResourceNamespace = string.Join(".", commonPathSegments.parts); return commonResourceNamespace; } private static string ExtractAssemblyRootNamespace(Assembly assembly) { var resources = assembly .GetTypes() .Where(x => !x.IsAnonymousType()) .Select(x => x.FullName) .ToList(); return ExtractCommonResourceNamespace(resources); } private static string GetResourceFileName(string resourceName) { var nameSegments = resourceName.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); var segmentCount = nameSegments.Count(); return (segmentCount < 2) ? string.Empty : string.Concat(nameSegments[segmentCount - 2], ".", nameSegments[segmentCount - 1]); } private static string GetResourceExtension(string resourceName) { var extension = Path.GetExtension(resourceName); return extension != null ? extension.Substring(1) : string.Empty; } } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/ISuperSimpleViewEngineMatcher.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { /// /// Matches and modifies the content of a rendered SuperSimpleViewEngine view. /// public interface ISuperSimpleViewEngineMatcher { /// /// Invokes the matcher on the content of the rendered view. /// /// The content of the rendered view. /// The model that was passed to the view. /// The host. /// The modified version of the view. string Invoke(string content, dynamic model, IViewEngineHost host); } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/IViewEngineHost.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { /// /// Provides the view engine with utility functions for /// encoding, locating partial view templates etc. /// public interface IViewEngineHost { /// /// Context object of the host application. /// /// An instance of the context object from the host. object Context { get; } /// /// Html "safe" encode a string /// /// Input string /// Encoded string string HtmlEncode(string input); /// /// Get the contents of a template /// /// Name/location of the template /// Model to use to locate the template via conventions /// Contents of the template, or null if not found string GetTemplate(string templateName, object model); /// /// Gets a uri string for a named route /// /// Named route name /// Parameters to use to expand the uri string /// Expanded uri string, or null if not found string GetUriString(string name, params string[] parameters); /// /// Expands a path to include any base paths /// /// Path to expand /// Expanded path string ExpandPath(string path); /// /// Get the anti forgery token form element /// /// String containing the form element string AntiForgeryToken(); } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/NancyViewEngineHost.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { using System; /// /// Nancy view engine host /// /// public class NancyViewEngineHost : IViewEngineHost { private IRenderContext renderContext; /// /// Initializes a new instance of the class, with /// the provided . /// /// /// The render context. /// public NancyViewEngineHost(IRenderContext renderContext) { this.renderContext = renderContext; this.Context = this.renderContext.Context; } /// /// Context object of the host application. /// /// An instance of the context object from the host. public object Context { get; private set; } /// /// Html "safe" encode a string /// /// Input string /// Encoded string public string HtmlEncode(string input) { return this.renderContext.HtmlEncode(input); } /// /// Get the contents of a template /// /// Name/location of the template /// Model to use to locate the template via conventions /// Contents of the template, or null if not found public string GetTemplate(string templateName, object model) { var viewLocationResult = this.renderContext.LocateView(templateName, model); if (viewLocationResult == null) { return "[ERR!]"; } using(var reader = viewLocationResult.Contents.Invoke()) return reader.ReadToEnd(); } /// /// Gets a uri string for a named route /// /// Named route name /// Parameters to use to expand the uri string /// Expanded uri string, or null if not found public string GetUriString(string name, params string[] parameters) { throw new NotImplementedException(); } /// /// Expands a path to include any base paths /// /// Path to expand /// Expanded path public string ExpandPath(string path) { return this.renderContext.ParsePath(path); } /// /// Get the anti forgery token form element /// /// String containing the form element public string AntiForgeryToken() { var tokenKeyValue = this.renderContext.GetCsrfToken(); return string.Format("", tokenKeyValue.Key, tokenKeyValue.Value); } } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngine.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { using Microsoft.CSharp.RuntimeBinder; using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Text; /// /// A super-simple view engine /// public class SuperSimpleViewEngine { /// /// Compiled Regex for viewbag substitutions /// private static readonly Regex ViewBagSubstitutionsRegEx = new Regex(@"@(?!)?ViewBag(?:\.(?[a-zA-Z0-9-_]+))*;?", RegexOptions.Compiled); /// /// Compiled Regex for single substitutions /// private static readonly Regex SingleSubstitutionsRegEx = new Regex(@"@(?!)?Model(?:\.(?[a-zA-Z0-9-_]+))*;?", RegexOptions.Compiled); /// /// Compiled Regex for context subsituations /// private static readonly Regex ContextSubstitutionsRegEx = new Regex(@"@(?!)?Context(?:\.(?[a-zA-Z0-9-_]+))*;?", RegexOptions.Compiled); /// /// Compiled Regex for each blocks /// private static readonly Regex EachSubstitutionRegEx = new Regex(@"@Each(?:\.(?(Model|Context)+))?(?:\.(?[a-zA-Z0-9-_]+))*;?(?.*?)@EndEach;?", RegexOptions.Compiled | RegexOptions.Singleline); /// /// Compiled Regex for each block current substitutions /// private static readonly Regex EachItemSubstitutionRegEx = new Regex(@"@(?!)?Current(?:\.(?[a-zA-Z0-9-_]+))*;?", RegexOptions.Compiled); /// /// Compiled Regex for if blocks /// private const string ConditionalOpenSyntaxPattern = @"@If(?Not)?(?Null)?(?:\.(?(Model|Context)+))?(?:\.(?[a-zA-Z0-9-_]+))+;?"; private const string ConditionalOpenInnerSyntaxPattern = @"@If(?:Not)?(?:Null)?(?:\.(?:(Model|Context)+))?(?:\.(?:[a-zA-Z0-9-_]+))+;?"; private const string ConditionalCloseStynaxPattern = @"@EndIf;?"; private static readonly string ConditionalSubstituionPattern = string.Format(@"{0}(?:[.*]|(?>{2}(?)|{1}(?<-DEPTH>)|.)*(?(DEPTH)(?!))){1}", ConditionalOpenSyntaxPattern, ConditionalCloseStynaxPattern, ConditionalOpenInnerSyntaxPattern); private static readonly Regex ConditionalSubstitutionRegEx = new Regex(ConditionalSubstituionPattern, RegexOptions.Compiled | RegexOptions.Singleline); /// /// Compiled regex for partial blocks /// private static readonly Regex PartialSubstitutionRegEx = new Regex(@"@Partial\['(?[^\]]+)'(?:.[ ]?@?(?(Model|Current)(?:\.(?[a-zA-Z0-9-_]+))*))?\];?", RegexOptions.Compiled); /// /// Compiled RegEx for section block declarations /// private static readonly Regex SectionDeclarationRegEx = new Regex(@"@Section\[\'(?.+?)\'\];?", RegexOptions.Compiled); /// /// Compiled RegEx for section block contents /// private static readonly Regex SectionContentsRegEx = new Regex(@"(?:@Section\[\'(?.+?)\'\];?(?.*?)@EndSection;?)", RegexOptions.Compiled | RegexOptions.Singleline); /// /// Compiled RegEx for master page declaration /// private static readonly Regex MasterPageHeaderRegEx = new Regex(@"^(?:@Master\[\'(?.+?)\'\]);?", RegexOptions.Compiled); /// /// Compiled RegEx for path expansion /// private static readonly Regex PathExpansionRegEx = new Regex(@"(?:@Path\[\'(?.+?)\'\]);?", RegexOptions.Compiled); /// /// Compiled RegEx for path expansion in attribute values /// private static readonly Regex AttributeValuePathExpansionRegEx = new Regex(@"(?[a-zA-Z]+)=(?[""'])(?~.+?)\k", RegexOptions.Compiled); /// /// Compiled RegEx for the CSRF anti forgery token /// private static readonly Regex AntiForgeryTokenRegEx = new Regex(@"@AntiForgeryToken;?", RegexOptions.Compiled); /// /// View engine transform processors /// private readonly List> processors; /// /// View engine extensions /// private readonly IEnumerable matchers; /// /// Initializes a new instance of the class. /// public SuperSimpleViewEngine() : this(Enumerable.Empty()) { } /// /// Initializes a new instance of the class, using /// the provided extensions. /// /// The matchers to use with the engine. public SuperSimpleViewEngine(IEnumerable matchers) { this.matchers = matchers ?? Enumerable.Empty(); this.processors = new List> { PerformViewBagSubstitutions, PerformSingleSubstitutions, PerformContextSubstitutions, PerformEachSubstitutions, PerformConditionalSubstitutions, PerformPathSubstitutions, PerformAntiForgeryTokenSubstitutions, this.PerformPartialSubstitutions, this.PerformMasterPageSubstitutions, }; } /// /// Renders a template /// /// The template to render. /// The model to user for rendering. /// The view engine host /// A string containing the expanded template. public string Render(string template, dynamic model, IViewEngineHost host) { var output = this.processors.Aggregate(template, (current, processor) => processor(current, model ?? new object(), host)); return this.matchers.Aggregate(output, (current, extension) => extension.Invoke(current, model, host)); } /// /// /// Gets a property value from the given model. /// /// /// Anonymous types, standard types and ExpandoObject are supported. /// Arbitrary dynamics (implementing IDynamicMetaObjectProvider) are not, unless /// they also implement IDictionary string, object for accessing properties. /// /// /// The model. /// The property name to evaluate. /// Tuple - Item1 being a bool for whether the evaluation was successful, Item2 being the value. /// Model type is not supported. private static Tuple GetPropertyValue(object model, string propertyName) { if (model == null || string.IsNullOrEmpty(propertyName)) { return new Tuple(false, null); } if (model is IDictionary) { return DynamicDictionaryPropertyEvaluator(model, propertyName); } if (!(model is IDynamicMetaObjectProvider)) { return StandardTypePropertyEvaluator(model, propertyName); } if (model is DynamicDictionaryValue) { var dynamicModel = model as DynamicDictionaryValue; return GetPropertyValue(dynamicModel.Value, propertyName); } if (model is DynamicObject) { return GetDynamicMember(model, propertyName); } throw new ArgumentException("model must be a standard type or implement IDictionary", "model"); } private static Tuple GetDynamicMember(object obj, string memberName) { var binder = Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, memberName, obj.GetType(), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var callsite = CallSite>.Create(binder); var result = callsite.Target(callsite, obj); return result == null ? new Tuple(false, null) : new Tuple(true, result); } /// /// A property extractor for standard types. /// /// The model. /// The property name. /// Tuple - Item1 being a bool for whether the evaluation was successful, Item2 being the value. private static Tuple StandardTypePropertyEvaluator(object model, string propertyName) { var type = model.GetType(); var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); var property = properties.Where(p => string.Equals(p.Name, propertyName, StringComparison.Ordinal)). FirstOrDefault(); if (property != null) { return new Tuple(true, property.GetValue(model, null)); } var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); var field = fields.Where(p => string.Equals(p.Name, propertyName, StringComparison.Ordinal)). FirstOrDefault(); return field == null ? new Tuple(false, null) : new Tuple(true, field.GetValue(model)); } /// /// A property extractor designed for ExpandoObject, but also for any /// type that implements IDictionary string object for accessing its /// properties. /// /// The model. /// The property name. /// Tuple - Item1 being a bool for whether the evaluation was successful, Item2 being the value. private static Tuple DynamicDictionaryPropertyEvaluator(object model, string propertyName) { var dictionaryModel = (IDictionary)model; object output; return !dictionaryModel.TryGetValue(propertyName, out output) ? new Tuple(false, null) : new Tuple(true, output); } /// /// Gets an IEnumerable of capture group values /// /// The match to use. /// Group name containing the capture group. /// IEnumerable of capture group values as strings. private static IEnumerable GetCaptureGroupValues(Match m, string groupName) { return m.Groups[groupName].Captures.Cast().Select(c => c.Value); } /// /// Gets a property value from a collection of nested parameter names /// /// The model containing properties. /// A collection of nested parameters (e.g. User, Name /// Tuple - Item1 being a bool for whether the evaluation was successful, Item2 being the value. private static Tuple GetPropertyValueFromParameterCollection(object model, IEnumerable parameters) { if (parameters == null) { return new Tuple(true, model); } var currentObject = model; foreach (var parameter in parameters) { var currentResult = GetPropertyValue(currentObject, parameter); if (currentResult.Item1 == false) { return new Tuple(false, null); } currentObject = currentResult.Item2; } return new Tuple(true, currentObject); } /// /// Gets the predicate result for an If or IfNot block /// /// The item to evaluate /// Property list to evaluate /// Whether to check for null, rather than straight boolean /// Bool representing the predicate result private static bool GetPredicateResult(object item, IEnumerable properties, bool nullCheck) { var substitutionObject = GetPropertyValueFromParameterCollection(item, properties); if (substitutionObject.Item1 == false && properties.Last().StartsWith("Has")) { var newProperties = properties.Take(properties.Count() - 1).Concat(new[] { properties.Last().Substring(3) }); substitutionObject = GetPropertyValueFromParameterCollection(item, newProperties); return GetHasPredicateResultFromSubstitutionObject(substitutionObject.Item2); } return GetPredicateResultFromSubstitutionObject(substitutionObject.Item2, nullCheck); } /// /// Returns the predicate result if the substitionObject is a valid bool /// /// The substitution object. /// /// Bool value of the substitutionObject, or false if unable to cast. private static bool GetPredicateResultFromSubstitutionObject(object substitutionObject, bool nullCheck) { if (nullCheck) { return substitutionObject == null; } if (substitutionObject != null && substitutionObject.GetType().GetProperty("Value") != null) { object value = ((dynamic)substitutionObject).Value; if (value is bool?) { substitutionObject = value; } } var predicateResult = false; var substitutionBool = substitutionObject as bool?; if (substitutionBool != null) { predicateResult = substitutionBool.Value; } return predicateResult; } /// /// Returns the predicate result if the substitionObject is a valid ICollection /// /// The substitution object. /// Bool value of the whether the ICollection has items, or false if unable to cast. private static bool GetHasPredicateResultFromSubstitutionObject(object substitutionObject) { var predicateResult = false; var substitutionCollection = substitutionObject as ICollection; if (substitutionCollection != null) { predicateResult = substitutionCollection.Count != 0; } return predicateResult; } /// /// Performs single @ViewBag.PropertyName substitutions. /// /// The template. /// This parameter is not used, the model is based on the "host.Context.ViewBag". /// View engine host /// Template with @ViewBag.PropertyName blocks expanded. private static string PerformViewBagSubstitutions(string template, object model, IViewEngineHost host) { return ViewBagSubstitutionsRegEx.Replace( template, m => { var properties = GetCaptureGroupValues(m, "ParameterName"); var substitution = GetPropertyValueFromParameterCollection(((dynamic)host.Context).ViewBag, properties); if (!substitution.Item1) { return "[ERR!]"; } if (substitution.Item2 == null) { return string.Empty; } return m.Groups["Encode"].Success ? host.HtmlEncode(substitution.Item2.ToString()) : substitution.Item2.ToString(); }); } /// /// Performs single @Model.PropertyName substitutions. /// /// The template. /// The model. /// View engine host /// Template with @Model.PropertyName blocks expanded. private static string PerformSingleSubstitutions(string template, object model, IViewEngineHost host) { return SingleSubstitutionsRegEx.Replace( template, m => { var properties = GetCaptureGroupValues(m, "ParameterName"); var substitution = GetPropertyValueFromParameterCollection(model, properties); if (!substitution.Item1) { return "[ERR!]"; } if (substitution.Item2 == null) { return string.Empty; } return m.Groups["Encode"].Success ? host.HtmlEncode(substitution.Item2.ToString()) : substitution.Item2.ToString(); }); } /// /// Performs single @Context.PropertyName substitutions. /// /// The template. /// The model. /// View engine host /// Template with @Context.PropertyName blocks expanded. private static string PerformContextSubstitutions(string template, object model, IViewEngineHost host) { return ContextSubstitutionsRegEx.Replace( template, m => { var properties = GetCaptureGroupValues(m, "ParameterName"); var substitution = GetPropertyValueFromParameterCollection(host.Context, properties); if (!substitution.Item1) { return "[ERR!]"; } if (substitution.Item2 == null) { return string.Empty; } return m.Groups["Encode"].Success ? host.HtmlEncode(substitution.Item2.ToString()) : substitution.Item2.ToString(); }); } /// /// Performs @Each.PropertyName substitutions /// /// The template. /// The model. /// View engine host /// Template with @Each.PropertyName blocks expanded. private string PerformEachSubstitutions(string template, object model, IViewEngineHost host) { return EachSubstitutionRegEx.Replace( template, m => { var properties = GetCaptureGroupValues(m, "ParameterName"); var modelSource = GetCaptureGroupValues(m, "ModelSource").SingleOrDefault(); if (modelSource != null && modelSource.Equals("Context", StringComparison.OrdinalIgnoreCase)) { model = host.Context; } var substitutionObject = GetPropertyValueFromParameterCollection(model, properties); if (substitutionObject.Item1 == false) { return "[ERR!]"; } if (substitutionObject.Item2 == null) { return string.Empty; } var substitutionEnumerable = substitutionObject.Item2 as IEnumerable; if (substitutionEnumerable == null) { return "[ERR!]"; } var contents = m.Groups["Contents"].Value; var result = new StringBuilder(); foreach (var item in substitutionEnumerable) { var modifiedContent = PerformPartialSubstitutions(contents, item, host); modifiedContent = PerformConditionalSubstitutions(modifiedContent, item, host); result.Append(ReplaceCurrentMatch(modifiedContent, item, host)); } return result.ToString(); }); } /// /// Expand a @Current match inside an @Each iterator /// /// Contents of the @Each block /// Current item from the @Each enumerable /// View engine host /// String result of the expansion of the @Each. private static string ReplaceCurrentMatch(string contents, object item, IViewEngineHost host) { return EachItemSubstitutionRegEx.Replace( contents, eachMatch => { if (string.IsNullOrEmpty(eachMatch.Groups["ParameterName"].Value)) { return eachMatch.Groups["Encode"].Success ? host.HtmlEncode(item.ToString()) : item.ToString(); } var properties = GetCaptureGroupValues(eachMatch, "ParameterName"); var substitution = GetPropertyValueFromParameterCollection(item, properties); if (!substitution.Item1) { return "[ERR!]"; } if (substitution.Item2 == null) { return string.Empty; } return eachMatch.Groups["Encode"].Success ? host.HtmlEncode(substitution.Item2.ToString()) : substitution.Item2.ToString(); }); } /// /// Performs @If.PropertyName and @IfNot.PropertyName substitutions /// /// The template. /// The model. /// View engine host /// Template with @If.PropertyName @IfNot.PropertyName blocks removed/expanded. private static string PerformConditionalSubstitutions(string template, object model, IViewEngineHost host) { var result = template; result = ConditionalSubstitutionRegEx.Replace( result, m => { var properties = GetCaptureGroupValues(m, "ParameterName"); var modelSource = GetCaptureGroupValues(m, "ModelSource").SingleOrDefault(); if (modelSource != null && modelSource.Equals("Context", StringComparison.OrdinalIgnoreCase)) { model = host.Context; } var predicateResult = GetPredicateResult(model, properties, m.Groups["Null"].Value == "Null"); if (m.Groups["Not"].Value == "Not") { predicateResult = !predicateResult; } return predicateResult ? PerformConditionalSubstitutions(m.Groups["Contents"].Value, model, host) : string.Empty; }); return result; } /// /// Perform path expansion substitutions /// /// The template. /// The model. /// View engine host /// Template with paths expanded private static string PerformPathSubstitutions(string template, object model, IViewEngineHost host) { var result = template; result = PathExpansionRegEx.Replace( result, m => { var path = m.Groups["Path"].Value; return host.ExpandPath(path); }); result = AttributeValuePathExpansionRegEx.Replace( result, m => { var attribute = m.Groups["Attribute"]; var quote = m.Groups["Quote"].Value; var path = m.Groups["Path"].Value; var expandedPath = host.ExpandPath(path); return string.Format("{0}={1}{2}{1}", attribute, quote, expandedPath); }); return result; } /// /// Perform CSRF anti forgery token expansions /// /// The template. /// The model. /// View engine host /// Template with anti forgery tokens expanded private static string PerformAntiForgeryTokenSubstitutions(string template, object model, IViewEngineHost host) { return AntiForgeryTokenRegEx.Replace(template, x => host.AntiForgeryToken()); } /// /// Perform @Partial partial view expansion /// /// The template. /// The model. /// View engine host /// Template with partials expanded private string PerformPartialSubstitutions(string template, dynamic model, IViewEngineHost host) { var result = template; result = PartialSubstitutionRegEx.Replace( result, m => { var partialViewName = ReplaceCurrentMatch(m.Groups["ViewName"].Value, model, host); var partialModel = model; var properties = GetCaptureGroupValues(m, "ParameterName"); if (m.Groups["Model"].Length > 0) { var modelValue = GetPropertyValueFromParameterCollection(partialModel, properties); if (modelValue.Item1 != true) { return "[ERR!]"; } partialModel = modelValue.Item2; } var partialTemplate = host.GetTemplate(partialViewName, partialModel); return this.Render(partialTemplate, partialModel, host); }); return result; } /// /// Invokes the master page rendering with current sections if necessary /// /// The template. /// The model. /// View engine host /// Template with master page applied and sections substituted private string PerformMasterPageSubstitutions(string template, object model, IViewEngineHost host) { var masterPageName = GetMasterPageName(template); if (string.IsNullOrWhiteSpace(masterPageName)) { return template; } var masterTemplate = host.GetTemplate(masterPageName, model); var sectionMatches = SectionContentsRegEx.Matches(template); var sections = sectionMatches.Cast().ToDictionary(sectionMatch => sectionMatch.Groups["SectionName"].Value, sectionMatch => sectionMatch.Groups["SectionContents"].Value); return this.RenderMasterPage(masterTemplate, sections, model, host); } /// /// Renders a master page - does a normal render then replaces any section tags with sections passed in /// /// The master page template /// Dictionary of section contents /// The model. /// View engine host /// Template with the master page applied and sections substituted private string RenderMasterPage(string masterTemplate, IDictionary sections, object model, IViewEngineHost host) { var result = this.Render(masterTemplate, model, host); result = SectionDeclarationRegEx.Replace( result, m => { var sectionName = m.Groups["SectionName"].Value; string sectionValue; return sections.TryGetValue(sectionName, out sectionValue) ? sectionValue : string.Empty; }); return result; } /// /// Gets the master page name, if one is specified /// /// The template /// Master page name or String.Empty private static string GetMasterPageName(string template) { using (var stringReader = new StringReader(template)) { var firstLine = stringReader.ReadLine(); if (firstLine == null) { return string.Empty; } var masterPageMatch = MasterPageHeaderRegEx.Match(firstLine); return masterPageMatch.Success ? masterPageMatch.Groups["MasterPage"].Value : string.Empty; } } } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngineRegistrations.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { using System.Collections.Generic; using Nancy.Bootstrapper; /// /// Performs application registrations for the SuperSimpleViewEngine. /// public class SuperSimpleViewEngineRegistrations : IRegistrations { private readonly ITypeCatalog typeCatalog; /// /// Initializes a new instance of the /// class. /// /// Type catalog. public SuperSimpleViewEngineRegistrations (ITypeCatalog typeCatalog) { this.typeCatalog = typeCatalog; } /// /// Gets the type registrations to register for this startup task /// public IEnumerable TypeRegistrations { get { return null; } } /// /// Gets the collection registrations to register for this startup task /// public IEnumerable CollectionTypeRegistrations { get { return new[] { new CollectionTypeRegistration(typeof(ISuperSimpleViewEngineMatcher), this.typeCatalog.GetTypesAssignableTo()) }; } } /// /// Gets the instance registrations to register for this startup task /// public IEnumerable InstanceRegistrations { get { return null; } } } } ================================================ FILE: src/Nancy/ViewEngines/SuperSimpleViewEngine/SuperSimpleViewEngineWrapper.cs ================================================ namespace Nancy.ViewEngines.SuperSimpleViewEngine { using System.Collections.Generic; using System.IO; using Nancy.Responses; /// /// Nancy IViewEngine wrapper for the super simple view engine /// public class SuperSimpleViewEngineWrapper : IViewEngine { /// /// Extensions that the view engine supports /// private readonly string[] extensions = new[] { "sshtml", "html", "htm" }; /// /// The engine itself /// private readonly SuperSimpleViewEngine viewEngine; /// /// Gets the extensions file extensions that are supported by the view engine. /// /// An instance containing the extensions. /// The extensions should not have a leading dot in the name. public IEnumerable Extensions { get { return this.extensions; } } /// /// Initializes a new instance of the class, using /// the provided extensions. /// /// The matchers to use with the engine. public SuperSimpleViewEngineWrapper(IEnumerable matchers) { this.viewEngine = new SuperSimpleViewEngine(matchers); } /// /// Initialise the view engine (if necessary) /// /// Startup context public void Initialize(ViewEngineStartupContext viewEngineStartupContext) { } /// /// Renders the view. /// /// A instance, containing information on how to get the view template. /// The model that should be passed into the view /// An instance. /// A response public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, IRenderContext renderContext) { return new HtmlResponse(contents: s => { var writer = new StreamWriter(s); var templateContents = renderContext.ViewCache.GetOrAdd(viewLocationResult, vr => { using (var reader = vr.Contents.Invoke()) return reader.ReadToEnd(); }); writer.Write(this.viewEngine.Render(templateContents, model, new NancyViewEngineHost(renderContext))); writer.Flush(); }); } } } ================================================ FILE: src/Nancy/ViewEngines/ViewEngineApplicationStartup.cs ================================================ namespace Nancy.ViewEngines { using System.Collections.Generic; using Nancy.Bootstrapper; /// /// Calls the initialize method on all implementations, at application startup. /// public class ViewEngineApplicationStartup : IApplicationStartup { private readonly IEnumerable viewEngines; private readonly IViewCache viewCache; private readonly IViewLocator viewLocator; /// /// Initializes a new instance of the class, with the /// provided , and . /// /// The available view engines. /// The view cache. /// The view locator. public ViewEngineApplicationStartup(IEnumerable viewEngines, IViewCache viewCache, IViewLocator viewLocator) { this.viewEngines = viewEngines; this.viewCache = viewCache; this.viewLocator = viewLocator; } /// /// Perform any initialisation tasks /// /// Application pipelines public void Initialize(IPipelines pipelines) { foreach (var viewEngine in viewEngines) { viewEngine.Initialize(CreateViewEngineStartupContext(viewEngine)); } } private ViewEngineStartupContext CreateViewEngineStartupContext(IViewEngine viewEngine) { return new ViewEngineStartupContext( this.viewCache, this.viewLocator); } } } ================================================ FILE: src/Nancy/ViewEngines/ViewEngineStartupContext.cs ================================================ namespace Nancy.ViewEngines { /// /// Context passed to each view engine on startup /// public class ViewEngineStartupContext { /// /// Initializes a new instance of the class, with /// the provided and . /// /// The view cache. /// The view locator. public ViewEngineStartupContext(IViewCache viewCache, IViewLocator viewLocator) { this.ViewLocator = viewLocator; this.ViewCache = viewCache; } /// /// Gets the Nancy view cache - can be used to precompile views at startup /// if necessary. /// public IViewCache ViewCache { get; private set; } /// /// Gets the Nancy view locator /// public IViewLocator ViewLocator { get; private set; } } } ================================================ FILE: src/Nancy/ViewEngines/ViewLocationContext.cs ================================================ namespace Nancy.ViewEngines { /// /// The context for which a view is being located. /// public class ViewLocationContext { /// /// The module path of the that is locating a view. /// /// A containing the module path. public string ModulePath { get; set; } /// /// The name of the that is locating a view. /// /// A containing the name of the module. public string ModuleName { get; set; } /// /// The request/response context /// public NancyContext Context{ get; set; } } } ================================================ FILE: src/Nancy/ViewEngines/ViewLocationResult.cs ================================================ namespace Nancy.ViewEngines { using System; using System.IO; /// /// Contains the result of an attempt to locate a view. /// public class ViewLocationResult : IEquatable { /// /// Initializes a new instance of the class. /// protected ViewLocationResult() { } /// /// Initializes a new instance of the class, with /// the provided , , /// and . /// /// The location of where the view was found. /// The name of the view. /// The file extension of the located view. /// A that can be used to read the contents of the located view. public ViewLocationResult(string location, string name, string extension, Func contents) { this.Location = location; this.Name = name; this.Extension = extension; this.Contents = contents; } /// /// Gets a function that produces a reader for retrieving the contents of the view. /// /// A instance that can be used to produce a reader for retrieving the contents of the view. public Func Contents { get; protected set; } /// /// Gets the extension of the view that was located. /// /// A containing the extension of the view that was located. /// The extension should not contain a leading dot. public string Extension { get; protected set; } /// /// Gets the location of where the view was found. /// /// A containing the location of the view. public string Location { get; protected set; } /// /// Gets the full name of the view that was found /// /// A containing the name of the view. public string Name { get; protected set; } /// /// Gets a value indicating whether the current item is stale /// /// True if stale, false otherwise public virtual bool IsStale() { return false; } /// /// Indicates whether the current object is equal to another object of the same type. /// /// if the current object is equal to the parameter; otherwise, . /// An to compare with this instance. public bool Equals(ViewLocationResult other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Equals(other.Extension, Extension) && Equals(other.Location, Location) && Equals(other.Name, Name); } /// /// Determines whether the specified is equal to the current . /// /// if the specified is equal to the current ; otherwise, . /// The to compare with the current . public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; return obj.GetType() == typeof (ViewLocationResult) && Equals((ViewLocationResult) obj); } /// /// Serves as a hash function for a particular type. /// /// A hash code for the current . public override int GetHashCode() { unchecked { var result = Extension.GetHashCode(); result = (result*397) ^ Location.GetHashCode(); result = (result*397) ^ Name.GetHashCode(); return result; } } /// /// Implements the operator ==. /// /// The left. /// The right. /// /// The result of the operator. /// public static bool operator ==(ViewLocationResult left, ViewLocationResult right) { return Equals(left, right); } /// /// Implements the operator !=. /// /// The left. /// The right. /// /// The result of the operator. /// public static bool operator !=(ViewLocationResult left, ViewLocationResult right) { return !Equals(left, right); } } } ================================================ FILE: src/Nancy/ViewEngines/ViewNotFoundException.cs ================================================ namespace Nancy.ViewEngines { using System; using System.Linq; /// /// Exception that is thrown when a view could not be located. /// public class ViewNotFoundException : Exception { private readonly IRootPathProvider rootPathProvider; /// /// Gets the name of the view. /// /// /// The name of the view. /// public string ViewName { get; private set; } /// /// Gets the available view engine extensions. /// /// /// The available view engine extensions. /// public string[] AvailableViewEngineExtensions { get; private set; } /// /// Gets the inspected locations. /// /// /// The inspected locations. /// public string[] InspectedLocations { get; private set; } private string message; /// /// Initializes a new instance of the , with /// the provided , , /// and . /// /// The name of the view that was being located. /// List of available view extensions that can be rendered by the available view engines. /// The locations that were inspected for the view. /// An instance. public ViewNotFoundException(string viewName, string[] availableViewEngineExtensions, string[] inspectedLocations, IRootPathProvider rootPathProvider) { this.rootPathProvider = rootPathProvider; this.ViewName = viewName; this.AvailableViewEngineExtensions = availableViewEngineExtensions; this.InspectedLocations = inspectedLocations; this.message = string.Format( "{4}Unable to locate requested view{4}{4} \u2022 Name: {0}{4} \u2022 Root path: {3}{4} \u2022 Supported extensions: {4}{1} \u2022 Inspected locations: {4}{2}{4}" + "If you were expecting raw data back, make sure you set the 'Accept'-header of the request to correct format, for example 'application/json'{4}", this.ViewName, string.Join(string.Empty, this.AvailableViewEngineExtensions.Select(x => string.Concat(" - ", x, Environment.NewLine))), string.Join(string.Empty, this.InspectedLocations.Select(x => string.Concat(" - ", x, Environment.NewLine))), this.rootPathProvider.GetRootPath(), Environment.NewLine); } /// /// Initializes a new instance of the class, with /// the provided and . /// /// The name of the view that was being located. /// List of available view extensions that can be rendered by the available view engines. public ViewNotFoundException(string viewName, string[] availableViewEngineExtensions) { this.ViewName = viewName; this.AvailableViewEngineExtensions = availableViewEngineExtensions; this.message = String.Format( "Unable to locate view '{0}'{2}Currently available view engine extensions: {1}{2}", this.ViewName, string.Join(",", this.AvailableViewEngineExtensions), Environment.NewLine); } /// /// Initializes a new instance of the class, with /// the provided . /// /// A message describing the problem public ViewNotFoundException(string msg) { this.message = msg; } /// /// Gets a message that describes the current exception. /// /// The error message that explains the reason for the exception, or an empty string(""). public override string Message { get { return message; } } } } ================================================ FILE: src/Nancy/ViewEngines/ViewRenderException.cs ================================================ namespace Nancy.ViewEngines.Razor { using System; /// /// An exception that indicates the view could not be rendered /// public class ViewRenderException : Exception { /// /// Create an instance of /// /// A description of the rendering problem public ViewRenderException(string msg) : base(msg) { } /// /// Create an instance of /// /// A description of the rendering problem /// The exception that is the cause of the current exception. public ViewRenderException(string msg, Exception innerException) : base(msg, innerException) { } } } ================================================ FILE: src/Nancy/ViewRenderer.cs ================================================ namespace Nancy { using System.IO; using Nancy.Responses.Negotiation; /// /// Helper class for rendering a view from a route handler. /// public class ViewRenderer : IHideObjectMembers { private readonly INancyModule module; /// /// Initializes a new instance of the class. /// /// The instance that is rendering the view. public ViewRenderer(INancyModule module) { this.module = module; } /// /// Renders the view with its name resolved from the model type, and model defined by the parameter. /// /// The model that should be passed into the view. /// A delegate that can be invoked with the that the view should be rendered to. /// The view name is model.GetType().Name with any Model suffix removed. public Negotiator this[dynamic model] { get { return this.GetNegotiator(null, model); } } /// /// Renders the view with the name defined by the parameter. /// /// The name of the view to render. /// A delegate that can be invoked with the that the view should be rendered to. /// The extension in the view name is optional. If it is omitted, then Nancy will try to resolve which of the available engines that should be used to render the view. public Negotiator this[string viewName] { get { return this.GetNegotiator(viewName, null); } } /// /// Renders the view with the name and model defined by the and parameters. /// /// The name of the view to render. /// The model that should be passed into the view. /// A delegate that can be invoked with the that the view should be rendered to. /// The extension in the view name is optional. If it is omitted, then Nancy will try to resolve which of the available engines that should be used to render the view. public Negotiator this[string viewName, dynamic model] { get { return this.GetNegotiator(viewName, model); } } private Negotiator GetNegotiator(string viewName, object model) { var negotiationContext = this.module.Context.NegotiationContext; negotiationContext.ViewName = viewName; negotiationContext.DefaultModel = model; negotiationContext.PermissableMediaRanges.Clear(); negotiationContext.PermissableMediaRanges.Add("text/html"); return new Negotiator(this.module.Context); } } } ================================================ FILE: src/Nancy/Xml/DefaultXmlConfigurationProvider.cs ================================================ namespace Nancy.Xml { using Nancy.Configuration; /// /// Provides the default configuration for . /// public class DefaultXmlConfigurationProvider : NancyDefaultConfigurationProvider { /// /// Gets the default configuration instance to register in the . /// /// The configuration instance /// Will return . public override XmlConfiguration GetDefaultConfiguration() { return XmlConfiguration.Default; } } } ================================================ FILE: src/Nancy/Xml/XmlConfiguration.cs ================================================ namespace Nancy.Xml { using System.Text; /// /// Configuration for XML serialization. /// public class XmlConfiguration { /// /// A default instance of the class. /// public static readonly XmlConfiguration Default = new XmlConfiguration { EncodingEnabled = false, DefaultEncoding = Encoding.UTF8 }; private XmlConfiguration() { } /// /// Initializes a new instance of the class. /// /// if encoding should be enabled, otherwise . /// The that should be used. public XmlConfiguration(bool encodingEnabled, Encoding defaultEncoding) { this.EncodingEnabled = encodingEnabled; this.DefaultEncoding = defaultEncoding ?? Default.DefaultEncoding; } /// /// Gets whether character encoding should be enabled, or not, for XML responses. /// /// if encoding is enabled, otherwise . /// The default value is . public bool EncodingEnabled { get; private set; } /// /// Gets the default encoding for XML responses. /// /// The used by default. /// The default value is . public Encoding DefaultEncoding { get; private set; } } } ================================================ FILE: src/Nancy/Xml/XmlConfigurationExtensions.cs ================================================ namespace Nancy.Xml { using System.Text; using Nancy.Configuration; /// /// Contains configuration extensions for . /// public static class XmlConfigurationExtensions { /// /// Configures . /// /// that should be configured. /// if encoding should be enabled, otherwise . /// The default . public static void Xml(this INancyEnvironment environment, bool enableEncoding, Encoding defaultEncoding = null) { environment.AddValue(new XmlConfiguration( enableEncoding, defaultEncoding)); } } } ================================================ FILE: src/Nancy.Authentication.Basic/BasicAuthentication.cs ================================================ namespace Nancy.Authentication.Basic { using System; using System.Text; using Nancy.Bootstrapper; using Nancy.Extensions; using Nancy.Security; /// /// Nancy basic authentication implementation /// public static class BasicAuthentication { private const string SCHEME = "Basic"; /// /// Enables basic authentication for the application /// /// Pipelines to add handlers to (usually "this") /// Forms authentication configuration public static void Enable(IPipelines pipelines, BasicAuthenticationConfiguration configuration) { if (pipelines == null) { throw new ArgumentNullException("pipelines"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } pipelines.BeforeRequest.AddItemToStartOfPipeline(GetCredentialRetrievalHook(configuration)); pipelines.AfterRequest.AddItemToEndOfPipeline(GetAuthenticationPromptHook(configuration)); } /// /// Enables basic authentication for a module /// /// Module to add handlers to (usually "this") /// Forms authentication configuration public static void Enable(INancyModule module, BasicAuthenticationConfiguration configuration) { if (module == null) { throw new ArgumentNullException("module"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } module.RequiresAuthentication(); module.Before.AddItemToStartOfPipeline(GetCredentialRetrievalHook(configuration)); module.After.AddItemToEndOfPipeline(GetAuthenticationPromptHook(configuration)); } /// /// Gets the pre request hook for loading the authenticated user's details /// from the auth header. /// /// Basic authentication configuration to use /// Pre request hook delegate private static Func GetCredentialRetrievalHook(BasicAuthenticationConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } return context => { RetrieveCredentials(context, configuration); return null; }; } private static Action GetAuthenticationPromptHook(BasicAuthenticationConfiguration configuration) { return context => { if (context.Response.StatusCode == HttpStatusCode.Unauthorized && SendAuthenticateResponseHeader(context, configuration)) { context.Response.Headers["WWW-Authenticate"] = String.Format("{0} realm=\"{1}\"", SCHEME, configuration.Realm); } }; } private static void RetrieveCredentials(NancyContext context, BasicAuthenticationConfiguration configuration) { var credentials = ExtractCredentialsFromHeaders(context.Request); if (credentials != null && credentials.Length == 2) { var user = configuration.UserValidator.Validate(credentials[0], credentials[1]); if (user != null) { context.CurrentUser = user; } } } private static string[] ExtractCredentialsFromHeaders(Request request) { var authorization = request.Headers.Authorization; if (string.IsNullOrEmpty(authorization)) { return null; } if (!authorization.StartsWith(SCHEME, StringComparison.OrdinalIgnoreCase)) { return null; } try { var encodedUserPass = authorization.Substring(SCHEME.Length).Trim(); var userPass = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUserPass)); return String.IsNullOrWhiteSpace(userPass) ? null : userPass.Split(new[] {':'}, 2); } catch (FormatException) { return null; } } private static bool SendAuthenticateResponseHeader(NancyContext context, BasicAuthenticationConfiguration configuration) { return configuration.UserPromptBehaviour == UserPromptBehaviour.Always || (configuration.UserPromptBehaviour == UserPromptBehaviour.NonAjax && !context.Request.IsAjaxRequest()); } } } ================================================ FILE: src/Nancy.Authentication.Basic/BasicAuthenticationConfiguration.cs ================================================ namespace Nancy.Authentication.Basic { using System; /// /// Configuration options for forms authentication /// public class BasicAuthenticationConfiguration { /// /// Initializes a new instance of the class. /// /// A valid instance of class /// Basic authentication realm /// Control when the browser should be instructed to prompt for credentials public BasicAuthenticationConfiguration(IUserValidator userValidator, string realm, UserPromptBehaviour userPromptBehaviour = UserPromptBehaviour.NonAjax) { if (userValidator == null) { throw new ArgumentNullException("userValidator"); } if (string.IsNullOrEmpty(realm)) { throw new ArgumentException("realm"); } this.UserValidator = userValidator; this.Realm = realm; this.UserPromptBehaviour = userPromptBehaviour; } /// /// Gets the user validator /// public IUserValidator UserValidator { get; private set; } /// /// Gets the basic authentication realm /// public string Realm { get; private set; } /// /// Determines when the browser should prompt the user for credentials /// public UserPromptBehaviour UserPromptBehaviour { get; private set; } } } ================================================ FILE: src/Nancy.Authentication.Basic/BasicHttpExtensions.cs ================================================ namespace Nancy.Authentication.Basic { using Nancy.Bootstrapper; /// /// Some simple helpers give some nice authentication syntax in the modules. /// public static class BasicHttpExtensions { /// /// Module requires basic authentication /// /// Module to enable /// Basic authentication configuration public static void EnableBasicAuthentication(this INancyModule module, BasicAuthenticationConfiguration configuration) { BasicAuthentication.Enable(module, configuration); } /// /// Module requires basic authentication /// /// Bootstrapper to enable /// Basic authentication configuration public static void EnableBasicAuthentication(this IPipelines pipeline, BasicAuthenticationConfiguration configuration) { BasicAuthentication.Enable(pipeline, configuration); } } } ================================================ FILE: src/Nancy.Authentication.Basic/IUserValidator.cs ================================================ namespace Nancy.Authentication.Basic { using System.Security.Claims; /// /// Provides a way to validate the username and password /// public interface IUserValidator { /// /// Validates the username and password /// /// Username /// Password /// A value representing the authenticated user, null if the user was not authenticated. ClaimsPrincipal Validate(string username, string password); } } ================================================ FILE: src/Nancy.Authentication.Basic/Nancy.Authentication.Basic.csproj ================================================  A basic HTTP authentication provider for Nancy. $(PackageTags);Authentication netstandard2.0;net452 ================================================ FILE: src/Nancy.Authentication.Basic/UserPromptBehaviour.cs ================================================ namespace Nancy.Authentication.Basic { /// /// Options to control when the browser prompts the user for credentials /// public enum UserPromptBehaviour { /// /// Never present user with login prompt /// Never, /// /// Always present user with login prompt /// Always, /// /// Only prompt the user for credentials on non-ajax requests /// NonAjax } } ================================================ FILE: src/Nancy.Authentication.Forms/FormsAuthentication.cs ================================================ namespace Nancy.Authentication.Forms { using System; using Bootstrapper; using Cookies; using Cryptography; using Extensions; using Helpers; using Security; /// /// Nancy forms authentication implementation /// public static class FormsAuthentication { private static string formsAuthenticationCookieName = "_ncfa"; // TODO - would prefer not to hold this here, but the redirect response needs it private static FormsAuthenticationConfiguration currentConfiguration; /// /// Gets or sets the forms authentication cookie name /// public static string FormsAuthenticationCookieName { get { return formsAuthenticationCookieName; } set { formsAuthenticationCookieName = value; } } /// /// Enables forms authentication for the application /// /// Pipelines to add handlers to (usually "this") /// Forms authentication configuration public static void Enable(IPipelines pipelines, FormsAuthenticationConfiguration configuration) { if (pipelines == null) { throw new ArgumentNullException("pipelines"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } configuration.EnsureConfigurationIsValid(); currentConfiguration = configuration; pipelines.BeforeRequest.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration)); if (!configuration.DisableRedirect) { pipelines.AfterRequest.AddItemToEndOfPipeline(GetRedirectToLoginHook(configuration)); } } /// /// Enables forms authentication for a module /// /// Module to add handlers to (usually "this") /// Forms authentication configuration public static void Enable(INancyModule module, FormsAuthenticationConfiguration configuration) { if (module == null) { throw new ArgumentNullException("module"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } configuration.EnsureConfigurationIsValid(); module.RequiresAuthentication(); currentConfiguration = configuration; module.Before.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration)); if (!configuration.DisableRedirect) { module.After.AddItemToEndOfPipeline(GetRedirectToLoginHook(configuration)); } } /// /// Creates a response that sets the authentication cookie and redirects /// the user back to where they came from. /// /// Current context /// User identifier guid /// Optional expiry date for the cookie (for 'Remember me') /// Url to redirect to if none in the querystring /// Nancy response with redirect. public static Response UserLoggedInRedirectResponse(NancyContext context, Guid userIdentifier, DateTime? cookieExpiry = null, string fallbackRedirectUrl = null) { var redirectUrl = fallbackRedirectUrl; if (string.IsNullOrEmpty(redirectUrl)) { redirectUrl = context.Request.Url.BasePath; } if (string.IsNullOrEmpty(redirectUrl)) { redirectUrl = "/"; } string redirectQuerystringKey = GetRedirectQuerystringKey(currentConfiguration); if (context.Request.Query[redirectQuerystringKey].HasValue) { var queryUrl = (string)context.Request.Query[redirectQuerystringKey]; if (context.IsLocalUrl(queryUrl)) { redirectUrl = queryUrl; } } var response = context.GetRedirect(redirectUrl); var authenticationCookie = BuildCookie(userIdentifier, cookieExpiry, currentConfiguration); response.WithCookie(authenticationCookie); return response; } /// /// Logs the user in. /// /// User identifier guid /// Optional expiry date for the cookie (for 'Remember me') /// Nancy response with status public static Response UserLoggedInResponse(Guid userIdentifier, DateTime? cookieExpiry = null) { var response = (Response)HttpStatusCode.OK; var authenticationCookie = BuildCookie(userIdentifier, cookieExpiry, currentConfiguration); response.WithCookie(authenticationCookie); return response; } /// /// Logs the user out and redirects them to a URL /// /// Current context /// URL to redirect to /// Nancy response public static Response LogOutAndRedirectResponse(NancyContext context, string redirectUrl) { var response = context.GetRedirect(redirectUrl); var authenticationCookie = BuildLogoutCookie(currentConfiguration); response.WithCookie(authenticationCookie); return response; } /// /// Logs the user out. /// /// Nancy response public static Response LogOutResponse() { var response = (Response)HttpStatusCode.OK; var authenticationCookie = BuildLogoutCookie(currentConfiguration); response.WithCookie(authenticationCookie); return response; } /// /// Gets the pre request hook for loading the authenticated user's details /// from the cookie. /// /// Forms authentication configuration to use /// Pre request hook delegate private static Func GetLoadAuthenticationHook(FormsAuthenticationConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } return context => { var userGuid = GetAuthenticatedUserFromCookie(context, configuration); if (userGuid != Guid.Empty) { context.CurrentUser = configuration.UserMapper.GetUserFromIdentifier(userGuid, context); } return null; }; } /// /// Gets the post request hook for redirecting to the login page /// /// Forms authentication configuration to use /// Post request hook delegate private static Action GetRedirectToLoginHook(FormsAuthenticationConfiguration configuration) { return context => { if (context.Response.StatusCode == HttpStatusCode.Unauthorized) { string redirectQuerystringKey = GetRedirectQuerystringKey(configuration); context.Response = context.GetRedirect( string.Format("{0}?{1}={2}", configuration.RedirectUrl, redirectQuerystringKey, context.ToFullPath("~" + context.Request.Path + HttpUtility.UrlEncode(context.Request.Url.Query)))); } }; } /// /// Gets the authenticated user GUID from the incoming request cookie if it exists /// and is valid. /// /// Current context /// Current configuration /// Returns user guid, or Guid.Empty if not present or invalid private static Guid GetAuthenticatedUserFromCookie(NancyContext context, FormsAuthenticationConfiguration configuration) { string cookieValueEncrypted; if (!context.Request.Cookies.TryGetValue(formsAuthenticationCookieName, out cookieValueEncrypted)) { return Guid.Empty; } if (string.IsNullOrEmpty(cookieValueEncrypted)) { return Guid.Empty; } var cookieValue = DecryptAndValidateAuthenticationCookie(cookieValueEncrypted, configuration); Guid returnGuid; if (string.IsNullOrEmpty(cookieValue) || !Guid.TryParse(cookieValue, out returnGuid)) { return Guid.Empty; } return returnGuid; } /// /// Build the forms authentication cookie /// /// Authenticated user identifier /// Optional expiry date for the cookie (for 'Remember me') /// Current configuration /// Nancy cookie instance private static INancyCookie BuildCookie(Guid userIdentifier, DateTime? cookieExpiry, FormsAuthenticationConfiguration configuration) { var cookieContents = EncryptAndSignCookie(userIdentifier.ToString(), configuration); var cookie = new NancyCookie(formsAuthenticationCookieName, cookieContents, true, configuration.RequiresSSL, cookieExpiry); if (!string.IsNullOrEmpty(configuration.Domain)) { cookie.Domain = configuration.Domain; } if (!string.IsNullOrEmpty(configuration.Path)) { cookie.Path = configuration.Path; } return cookie; } /// /// Builds a cookie for logging a user out /// /// Current configuration /// Nancy cookie instance private static INancyCookie BuildLogoutCookie(FormsAuthenticationConfiguration configuration) { var cookie = new NancyCookie(formsAuthenticationCookieName, String.Empty, true, configuration.RequiresSSL, DateTime.Now.AddDays(-1)); if (!string.IsNullOrEmpty(configuration.Domain)) { cookie.Domain = configuration.Domain; } if (!string.IsNullOrEmpty(configuration.Path)) { cookie.Path = configuration.Path; } return cookie; } /// /// Encrypt and sign the cookie contents /// /// Plain text cookie value /// Current configuration /// Encrypted and signed string private static string EncryptAndSignCookie(string cookieValue, FormsAuthenticationConfiguration configuration) { var encryptedCookie = configuration.CryptographyConfiguration.EncryptionProvider.Encrypt(cookieValue); var hmacBytes = GenerateHmac(encryptedCookie, configuration); var hmacString = Convert.ToBase64String(hmacBytes); return String.Format("{1}{0}", encryptedCookie, hmacString); } /// /// Generate a hmac for the encrypted cookie string /// /// Encrypted cookie string /// Current configuration /// Hmac byte array private static byte[] GenerateHmac(string encryptedCookie, FormsAuthenticationConfiguration configuration) { return configuration.CryptographyConfiguration.HmacProvider.GenerateHmac(encryptedCookie); } /// /// Decrypt and validate an encrypted and signed cookie value /// /// Encrypted and signed cookie value /// Current configuration /// Decrypted value, or empty on error or if failed validation public static string DecryptAndValidateAuthenticationCookie(string cookieValue, FormsAuthenticationConfiguration configuration) { var hmacStringLength = Base64Helpers.GetBase64Length(configuration.CryptographyConfiguration.HmacProvider.HmacLength); var encryptedCookie = cookieValue.Substring(hmacStringLength); var hmacString = cookieValue.Substring(0, hmacStringLength); var encryptionProvider = configuration.CryptographyConfiguration.EncryptionProvider; // Check the hmacs, but don't early exit if they don't match var hmacBytes = Convert.FromBase64String(hmacString); var newHmac = GenerateHmac(encryptedCookie, configuration); var hmacValid = HmacComparer.Compare(newHmac, hmacBytes, configuration.CryptographyConfiguration.HmacProvider.HmacLength); var decrypted = encryptionProvider.Decrypt(encryptedCookie); // Only return the decrypted result if the hmac was ok return hmacValid ? decrypted : string.Empty; } /// /// Gets the redirect query string key from /// /// The forms authentication configuration. /// Redirect Querystring key private static string GetRedirectQuerystringKey(FormsAuthenticationConfiguration configuration) { string redirectQuerystringKey = null; if (configuration != null) { redirectQuerystringKey = configuration.RedirectQuerystringKey; } if (string.IsNullOrWhiteSpace(redirectQuerystringKey)) { redirectQuerystringKey = FormsAuthenticationConfiguration.DefaultRedirectQuerystringKey; } return redirectQuerystringKey; } } } ================================================ FILE: src/Nancy.Authentication.Forms/FormsAuthenticationConfiguration.cs ================================================ namespace Nancy.Authentication.Forms { using System; using Cryptography; /// /// Configuration options for forms authentication /// public class FormsAuthenticationConfiguration { internal const string DefaultRedirectQuerystringKey = "returnUrl"; /// /// Initializes a new instance of the class. /// public FormsAuthenticationConfiguration() : this(CryptographyConfiguration.Default) { } /// /// Initializes a new instance of the class. /// /// Cryptography configuration public FormsAuthenticationConfiguration(CryptographyConfiguration cryptographyConfiguration) { CryptographyConfiguration = cryptographyConfiguration; RedirectQuerystringKey = DefaultRedirectQuerystringKey; } /// /// Gets or sets the forms authentication query string key for storing the return url /// public string RedirectQuerystringKey { get; set; } /// /// Gets or sets the redirect url for pages that require authentication /// public string RedirectUrl { get; set; } /// /// Gets or sets the username/identifier mapper /// public IUserMapper UserMapper { get; set; } /// /// Gets or sets RequiresSSL property /// /// The flag that indicates whether SSL is required public bool RequiresSSL { get; set; } /// /// Gets or sets whether to redirect to login page during unauthorized access. /// public bool DisableRedirect { get; set; } /// /// Gets or sets the domain of the auth cookie /// public string Domain { get; set; } /// /// Gets or sets the path of the auth cookie /// public string Path { get; set; } /// /// Gets or sets the cryptography configuration /// public CryptographyConfiguration CryptographyConfiguration { get; set; } /// /// Ensures the configuration is valid or not. /// /// public virtual void EnsureConfigurationIsValid() { if (!this.DisableRedirect && string.IsNullOrEmpty(this.RedirectUrl)) { throw new InvalidOperationException("When DisableRedirect is false RedirectUrl cannot be null."); } if (this.UserMapper == null) { throw new InvalidOperationException("UserMapper cannot be null."); } if (this.CryptographyConfiguration == null) { throw new InvalidOperationException("CryptographyConfiguration cannot be null."); } if (this.CryptographyConfiguration.EncryptionProvider == null) { throw new InvalidOperationException("CryptographyConfiguration EncryptionProvider cannot be null."); } if (this.CryptographyConfiguration.HmacProvider == null) { throw new InvalidOperationException("CryptographyConfiguration HmacProvider cannot be null."); } } } } ================================================ FILE: src/Nancy.Authentication.Forms/IUserMapper.cs ================================================ namespace Nancy.Authentication.Forms { using System; using System.Security.Claims; /// /// Provides a mapping between forms auth guid identifiers and /// real usernames /// public interface IUserMapper { /// /// Get the real username from an identifier /// /// User identifier /// The current NancyFx context /// Matching populated IUserIdentity object, or empty ClaimsPrincipal GetUserFromIdentifier(Guid identifier, NancyContext context); } } ================================================ FILE: src/Nancy.Authentication.Forms/ModuleExtensions.cs ================================================ namespace Nancy.Authentication.Forms { using System; using Nancy.Extensions; /// /// Module extensions for login/logout of forms auth /// public static class ModuleExtensions { /// /// Logs the user in and returns either an empty 200 response for ajax requests, or a redirect response for non-ajax. /// /// Nancy module /// User identifier guid /// Optional expiry date for the cookie (for 'Remember me') /// Url to redirect to if none in the querystring /// Nancy response with redirect if request was not ajax, otherwise with OK. public static Response Login(this INancyModule module, Guid userIdentifier, DateTime? cookieExpiry = null, string fallbackRedirectUrl = "/") { return module.Context.Request.IsAjaxRequest() ? LoginWithoutRedirect(module, userIdentifier, cookieExpiry) : LoginAndRedirect(module, userIdentifier, cookieExpiry, fallbackRedirectUrl); } /// /// Logs the user in with the given user guid and redirects. /// /// Nancy module /// User identifier guid /// Optional expiry date for the cookie (for 'Remember me') /// Url to redirect to if none in the querystring /// Nancy response instance public static Response LoginAndRedirect(this INancyModule module, Guid userIdentifier, DateTime? cookieExpiry = null, string fallbackRedirectUrl = "/") { return FormsAuthentication.UserLoggedInRedirectResponse(module.Context, userIdentifier, cookieExpiry, fallbackRedirectUrl); } /// /// Logs the user in with the given user guid and returns ok response. /// /// Nancy module /// User identifier guid /// Optional expiry date for the cookie (for 'Remember me') /// Nancy response instance public static Response LoginWithoutRedirect(this INancyModule module, Guid userIdentifier, DateTime? cookieExpiry = null) { return FormsAuthentication.UserLoggedInResponse(userIdentifier, cookieExpiry); } /// /// Logs the user out and returns either an empty 200 response for ajax requests, or a redirect response for non-ajax. /// /// Nancy module /// URL to redirect to /// Nancy response with redirect if request was not ajax, otherwise with OK. public static Response Logout(this INancyModule module, string redirectUrl) { return module.Context.Request.IsAjaxRequest() ? FormsAuthentication.LogOutResponse() : FormsAuthentication.LogOutAndRedirectResponse(module.Context, redirectUrl); } /// /// Logs the user out and redirects /// /// Nancy module /// URL to redirect to /// Nancy response instance public static Response LogoutAndRedirect(this INancyModule module, string redirectUrl) { return FormsAuthentication.LogOutAndRedirectResponse(module.Context, redirectUrl); } /// /// Logs the user out without a redirect /// /// Nancy module /// Nancy response instance public static Response LogoutWithoutRedirect(this INancyModule module) { return FormsAuthentication.LogOutResponse(); } } } ================================================ FILE: src/Nancy.Authentication.Forms/Nancy.Authentication.Forms.csproj ================================================  A forms authentication provider for Nancy. $(PackageTags);Authentication netstandard2.0;net452 ================================================ FILE: src/Nancy.Authentication.Stateless/Nancy.Authentication.Stateless.csproj ================================================  A stateless authentication provider for Nancy. $(PackageTags);Authentication netstandard2.0;net452 ================================================ FILE: src/Nancy.Authentication.Stateless/StatelessAuthentication.cs ================================================ namespace Nancy.Authentication.Stateless { using System; using Nancy.Bootstrapper; /// /// Nancy stateless authentication implementation /// public static class StatelessAuthentication { /// /// Enables stateless authentication for the application /// /// Pipelines to add handlers to (usually "this") /// Stateless authentication configuration public static void Enable(IPipelines pipelines, StatelessAuthenticationConfiguration configuration) { if (pipelines == null) { throw new ArgumentNullException("pipelines"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } if (!configuration.IsValid) { throw new ArgumentException("Configuration is invalid", "configuration"); } pipelines.BeforeRequest.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration)); } /// /// Enables stateless authentication for a module /// /// Module to add handlers to (usually "this") /// Stateless authentication configuration public static void Enable(INancyModule module, StatelessAuthenticationConfiguration configuration) { if (module == null) { throw new ArgumentNullException("module"); } if (configuration == null) { throw new ArgumentNullException("configuration"); } if (!configuration.IsValid) { throw new ArgumentException("Configuration is invalid", "configuration"); } module.Before.AddItemToStartOfPipeline(GetLoadAuthenticationHook(configuration)); } /// /// Gets the pre request hook for loading the authenticated user's details /// from apikey given in request. /// /// Stateless authentication configuration to use /// Pre request hook delegate private static Func GetLoadAuthenticationHook(StatelessAuthenticationConfiguration configuration) { if (configuration == null) { throw new ArgumentNullException("configuration"); } return context => { context.CurrentUser = configuration.GetUserIdentity(context); return null; }; } } } ================================================ FILE: src/Nancy.Authentication.Stateless/StatelessAuthenticationConfiguration.cs ================================================ namespace Nancy.Authentication.Stateless { using System; using System.Security.Claims; /// /// Configuration options for stateless authentication /// public class StatelessAuthenticationConfiguration { internal readonly Func GetUserIdentity; /// /// Initializes a new instance of the class. /// public StatelessAuthenticationConfiguration(Func getUserIdentity) { this.GetUserIdentity = getUserIdentity; } /// /// Gets a value indicating whether the configuration is valid or not. /// public virtual bool IsValid { get { return this.GetUserIdentity != null; } } } } ================================================ FILE: src/Nancy.Embedded/Conventions/EmbeddedStaticContentConventionBuilder.cs ================================================ namespace Nancy.Embedded.Conventions { using System; using System.Collections.Concurrent; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using Nancy.Helpers; using Nancy.Responses; public class EmbeddedStaticContentConventionBuilder { private static readonly ConcurrentDictionary> ResponseFactoryCache; private static readonly Regex PathReplaceRegex = new Regex(@"[/\\]", RegexOptions.Compiled); static EmbeddedStaticContentConventionBuilder() { ResponseFactoryCache = new ConcurrentDictionary>(); } /// /// Adds a directory-based convention for embedded static convention. /// /// The path that should be matched with the request. /// The path to where the content is stored in your application, relative to the root. If this is then it will be the same as . /// A list of extensions that is valid for the conventions. If not supplied, all extensions are valid. /// A instance for the requested embedded static contents if it was found, otherwise . public static Func AddDirectory(string requestedPath, Assembly assembly, string contentPath = null, params string[] allowedExtensions) { if (!requestedPath.StartsWith("/")) { requestedPath = string.Concat("/", requestedPath); } return (ctx, root) => { var path = HttpUtility.UrlDecode(ctx.Request.Path); var fileName = Path.GetFileName(path); if (string.IsNullOrEmpty(fileName)) { return null; } var pathWithoutFilename = GetPathWithoutFilename(fileName, path); if (!pathWithoutFilename.StartsWith(requestedPath, StringComparison.OrdinalIgnoreCase)) { ctx.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] The requested resource '", path, "' does not match convention mapped to '", requestedPath, "'"))); return null; } contentPath = GetContentPath(requestedPath, contentPath); var responseFactory = ResponseFactoryCache.GetOrAdd(path, BuildContentDelegate(ctx, requestedPath, contentPath, assembly, allowedExtensions)); return responseFactory.Invoke(); }; } private static Func> BuildContentDelegate(NancyContext context, string requestedPath, string contentPath, Assembly assembly, string[] allowedExtensions) { return requestPath => { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] Attempting to resolve embedded static content '", requestPath, "'"))); var extension = Path.GetExtension(requestPath); if (allowedExtensions.Length != 0 && !allowedExtensions.Any(e => string.Equals(e, extension, StringComparison.OrdinalIgnoreCase))) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] The requested extension '", extension, "' does not match any of the valid extensions for the convention '", string.Join(",", allowedExtensions), "'"))); return () => null; } var fullFilePath = Path.GetFullPath(GetSafeRequestPath(requestPath, requestedPath, contentPath)); var relativeFilePath = ResolveRelativeFilePath(fullFilePath); var fileName = Path.Combine( Path.DirectorySeparatorChar.ToString(), GetEncodedPath(relativeFilePath)); var contentRootPath = Path.Combine( Path.DirectorySeparatorChar.ToString(), GetEncodedPath(contentPath)); if (!IsWithinContentFolder(contentRootPath, fileName)) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] The request '", fileName, "' is trying to access a path outside the content folder '", contentPath, "'"))); return () => null; } var resourceName = Path.GetDirectoryName(assembly.GetName().Name + fileName).Replace(Path.DirectorySeparatorChar, '.').Replace('-', '_'); fileName = Path.GetFileName(fileName); if (!assembly.GetManifestResourceNames().Any(x => string.Equals(x, resourceName + "." + fileName, StringComparison.OrdinalIgnoreCase))) { context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] The requested resource '", requestPath, "' was not found in assembly '", assembly.GetName().Name, "'"))); return () => null; } context.Trace.TraceLog.WriteLog(x => x.AppendLine(string.Concat("[EmbeddedStaticContentConventionBuilder] Returning file '", fileName, "'"))); return () => new EmbeddedFileResponse(assembly, resourceName, fileName); }; } private static string ResolveRelativeFilePath(string fullFilePath) { var fileRootPath = Directory.GetDirectoryRoot(fullFilePath); return fullFilePath.Substring(fileRootPath.Length); } private static string GetEncodedPath(string path) { return PathReplaceRegex.Replace(path.TrimStart(new[] { '/' }), Path.DirectorySeparatorChar.ToString()); } private static string GetSafeRequestPath(string requestPath, string requestedPath, string contentPath) { var actualContentPath = (contentPath.Equals("/") ? string.Empty : contentPath); if (requestedPath.Equals("/")) { return string.Concat(actualContentPath, requestPath); } var expression = new Regex(Regex.Escape(requestedPath), RegexOptions.IgnoreCase); return expression.Replace(requestPath, actualContentPath, 1); } private static string GetContentPath(string requestedPath, string contentPath) { contentPath = contentPath ?? requestedPath; if (!contentPath.StartsWith("/")) { contentPath = string.Concat("/", contentPath); } return contentPath; } private static string GetPathWithoutFilename(string fileName, string path) { var pathWithoutFileName = path.Replace(fileName, string.Empty); return (pathWithoutFileName.Equals("/")) ? pathWithoutFileName : pathWithoutFileName.TrimEnd(new[] { '/' }); } /// /// Returns whether the given filename is contained within the content folder /// /// Content root path /// Filename requested /// True if contained within the content root, false otherwise private static bool IsWithinContentFolder(string contentRootPath, string fileName) { return fileName.StartsWith(contentRootPath, StringComparison.Ordinal); } } } ================================================ FILE: src/Nancy.Embedded/Nancy.Embedded.csproj ================================================  Helpers for serving embedded static content with Nancy. $(PackageTags);Embedded;Static Content netstandard2.0;net452 ================================================ FILE: src/Nancy.Encryption.MachineKey/MachineKeyCryptographyConfigurations.cs ================================================ namespace Nancy.Encryption.MachineKey { using System; using Nancy.Cryptography; /// /// Helpers for creating crypto configs from machine.config crypto types /// public static class MachineKeyCryptographyConfigurations { private static readonly Lazy DefaultConfiguration = new Lazy(() => new CryptographyConfiguration( new MachineKeyEncryptionProvider(), new MachineKeyHmacProvider())); private static readonly Lazy NoEncryptionConfiguration = new Lazy(() => new CryptographyConfiguration( new NoEncryptionProvider(), new MachineKeyHmacProvider())); /// /// Gets the default configuration for machinekey encryption. /// Uses the machine key for both encryption and hmac generation /// public static CryptographyConfiguration Default { get { return DefaultConfiguration.Value; } } /// /// Gets the configuration to use machine key for HMAC, but no encryption /// public static CryptographyConfiguration NoEncryption { get { return NoEncryptionConfiguration.Value; } } } } ================================================ FILE: src/Nancy.Encryption.MachineKey/MachineKeyEncryptionProvider.cs ================================================ namespace Nancy.Encryption.MachineKey { using System; using System.Text; using System.Web.Security; using Nancy.Cryptography; /// /// An encryption provider that uses the ASP.Net MachineKey /// public class MachineKeyEncryptionProvider : IEncryptionProvider { /// /// Encrypt and base64 encode the string /// /// Data to encrypt /// Encrypted string public string Encrypt(string data) { var input = Encoding.UTF8.GetBytes(data); return MachineKey.Encode(input, MachineKeyProtection.Encryption); } /// /// Decrypt string /// /// Data to decrypt /// Decrypted string public string Decrypt(string data) { try { var bytes = MachineKey.Decode(data, MachineKeyProtection.Encryption); return Encoding.UTF8.GetString(bytes); } catch (Exception) { return string.Empty; } } } } ================================================ FILE: src/Nancy.Encryption.MachineKey/MachineKeyHmacProvider.cs ================================================ namespace Nancy.Encryption.MachineKey { using System; using System.Configuration; using System.Globalization; using System.Linq; using System.Reflection; using System.Text; using System.Web.Configuration; using System.Web.Security; using Nancy.Cryptography; /// /// A HMAC provider using the ASP.Net Machine key /// /// This is hacky as anything because of all kinds of horrible /// internal sealed nonsense inside the framework. /// public class MachineKeyHmacProvider : IHmacProvider { /// /// Gets the length of the HMAC signature in bytes /// public int HmacLength { get; private set; } public MachineKeyHmacProvider() { this.GetHmacLength(); } /// /// Create a hmac from the given data /// /// Data to create hmac from /// Hmac bytes public byte[] GenerateHmac(string data) { var input = Encoding.UTF8.GetBytes(data); return this.GenerateHmac(input); } /// /// Create a hmac from the given data /// /// Data to create hmac from /// Hmac bytes public byte[] GenerateHmac(byte[] data) { var encoded = MachineKey.Encode(data, MachineKeyProtection.Validation); var bytes = HexStringToByteArray(encoded); return bytes.Skip(bytes.Length - HmacLength).ToArray(); } /// /// Uses reflection to get the hmac length that machine key will generate /// private void GetHmacLength() { // Yucky reflection because it doesn't expose this for some reason :( var machineKeyConfig = (MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey"); var machineKeySectionType = machineKeyConfig.GetType(); var field = machineKeySectionType.GetField("_HashSize", BindingFlags.NonPublic | BindingFlags.Static); try { this.HmacLength = (int)field.GetValue(null); } catch (Exception) { throw new InvalidOperationException("Unable to retrieve hash size from machine.config"); } } /// /// Converts a string of "hex bytes" to actual bytes. /// We could just use the same method as .net does but /// like a lot of useful things in .net, it's internal. /// /// String of hex bytes /// Actual byte array private static byte[] HexStringToByteArray(string data) { if (string.IsNullOrEmpty(data) || data.Length % 2 != 0) { return ArrayCache.Empty(); } var output = new byte[data.Length / 2]; for (var i = 0; i < data.Length / 2; i++) { var numberString = new string(new[] { data[2 * i], data[2 * i + 1] }); var value = byte.Parse(numberString, NumberStyles.HexNumber); output[i] = value; } return output; } } } ================================================ FILE: src/Nancy.Encryption.MachineKey/Nancy.Encryption.MachineKey.csproj ================================================  System.Web MachineKey encrypton and HMAC providers for Nancy. $(PackageTags);Encryption net452 ================================================ FILE: src/Nancy.Hosting.Aspnet/AspNetRootPathProvider.cs ================================================ namespace Nancy.Hosting.Aspnet { using System.Web.Hosting; public class AspNetRootPathProvider : IRootPathProvider { public string GetRootPath() { return HostingEnvironment.MapPath("~/"); } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/BootstrapperEntry.cs ================================================ namespace Nancy.Hosting.Aspnet { public sealed class BootstrapperEntry { public BootstrapperEntry(string assembly, string name) { Assembly = assembly; Name = name; } public string Assembly { get; private set; } public string Name { get; private set; } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/DefaultNancyAspNetBootstrapper.cs ================================================ namespace Nancy.Hosting.Aspnet { using System; using System.Collections.Generic; using System.Linq; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Diagnostics; using Nancy.TinyIoc; /// /// TinyIoC ASP.Net Bootstrapper /// No child container support because per-request is handled by the AsPerRequestSingleton option /// public abstract class DefaultNancyAspNetBootstrapper : NancyBootstrapperBase { /// /// Gets the diagnostics for initialisation /// /// IDiagnostics implementation protected override IDiagnostics GetDiagnostics() { return this.ApplicationContainer.Resolve(); } /// /// Gets all request startup tasks /// protected override IEnumerable RequestStartupTasks { get { var types = base.RequestStartupTasks.ToArray(); this.ApplicationContainer.RegisterMultiple(typeof(IRequestStartup), types).AsPerRequestSingleton(); return types; } } /// /// Gets all registered startup tasks /// /// An instance containing instances. protected override IEnumerable GetApplicationStartupTasks() { return this.ApplicationContainer.ResolveAll(false); } /// /// Resolves all request startup tasks /// /// Container to use /// Types to register - not used /// An instance containing instances. protected override IEnumerable RegisterAndGetRequestStartupTasks(TinyIoCContainer container, Type[] requestStartupTypes) { return container.ResolveAll(); } /// /// Gets all registered application registration tasks /// /// An instance containing instances. protected override IEnumerable GetRegistrationTasks() { return this.ApplicationContainer.ResolveAll(false); } /// /// Get all NancyModule implementation instances - should be multi-instance /// /// Current request context /// IEnumerable of INancyModule public sealed override IEnumerable GetAllModules(NancyContext context) { return this.ApplicationContainer.ResolveAll(false); } /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance public override INancyModule GetModule(Type moduleType, NancyContext context) { return this.ApplicationContainer.Resolve(moduleType.FullName); } /// /// Creates and initializes the request pipelines. /// /// The used by the request. /// An instance. protected sealed override IPipelines InitializeRequestPipelines(NancyContext context) { return base.InitializeRequestPipelines(context); } /// /// Configures the container using AutoRegister followed by registration /// of default INancyModuleCatalog and IRouteResolver. /// /// Container instance protected override void ConfigureApplicationContainer(TinyIoCContainer container) { container.AutoRegister(); container.Register(this); } /// /// Resolve INancyEngine /// /// INancyEngine implementation protected sealed override INancyEngine GetEngineInternal() { return this.ApplicationContainer.Resolve(); } /// /// Create a default, unconfigured, container /// /// Container instance protected override TinyIoCContainer GetApplicationContainer() { return new TinyIoCContainer(); } /// /// Register the bootstrapper's implemented types into the container. /// This is necessary so a user can pass in a populated container but not have /// to take the responsibility of registering things like INancyModuleCatalog manually. /// /// Application container to register into protected sealed override void RegisterBootstrapperTypes(TinyIoCContainer applicationContainer) { applicationContainer.Register(this); } /// /// Registers an instance in the container. /// /// The container to register into. /// The instance to register. protected override void RegisterNancyEnvironment(TinyIoCContainer container, INancyEnvironment environment) { container.Register(environment); } /// /// Gets the used by the application. /// /// An instance. protected override INancyEnvironmentConfigurator GetEnvironmentConfigurator() { return this.ApplicationContainer.Resolve(); } /// /// Get the instance. /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. public override INancyEnvironment GetEnvironment() { return this.ApplicationContainer.Resolve(); } /// /// Register the default implementations of internally used types into the container as singletons /// /// Container to register into /// Type registrations to register protected sealed override void RegisterTypes(TinyIoCContainer container, IEnumerable typeRegistrations) { foreach (var typeRegistration in typeRegistrations) { switch (typeRegistration.Lifetime) { case Lifetime.Transient: container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsMultiInstance(); break; case Lifetime.Singleton: container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsSingleton(); break; case Lifetime.PerRequest: container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsPerRequestSingleton(); break; default: throw new ArgumentOutOfRangeException(); } } } /// /// Register the various collections into the container as singletons to later be resolved /// by IEnumerable{Type} constructor dependencies. /// /// Container to register into /// Collection type registrations to register protected sealed override void RegisterCollectionTypes(TinyIoCContainer container, IEnumerable collectionTypeRegistrations) { foreach (var collectionTypeRegistration in collectionTypeRegistrations) { switch (collectionTypeRegistration.Lifetime) { case Lifetime.Transient: container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes).AsMultiInstance(); break; case Lifetime.Singleton: container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes).AsSingleton(); break; case Lifetime.PerRequest: container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes).AsPerRequestSingleton(); break; default: throw new ArgumentOutOfRangeException(); } } } /// /// Register the given module types into the container /// /// Container to register into /// NancyModule types protected sealed override void RegisterModules(TinyIoCContainer container, IEnumerable moduleRegistrationTypes) { foreach (var registrationType in moduleRegistrationTypes) { container.Register(typeof(INancyModule), registrationType.ModuleType, registrationType.ModuleType.FullName).AsPerRequestSingleton(); } } /// /// Register the given instances into the container /// /// Container to register into /// Instance registration types protected override void RegisterInstances(TinyIoCContainer container, IEnumerable instanceRegistrations) { foreach (var instanceRegistration in instanceRegistrations) { container.Register( instanceRegistration.RegistrationType, instanceRegistration.Implementation); } } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/Nancy.Hosting.Aspnet.csproj ================================================  Enables hosting Nancy on ASP.NET. $(PackageTags);Host net452 content true ================================================ FILE: src/Nancy.Hosting.Aspnet/NancyFxSection.cs ================================================ namespace Nancy.Hosting.Aspnet { using System; using System.Configuration; public class NancyFxSection : ConfigurationSection { [ConfigurationProperty("disableoutputbuffer")] public DisableOutputBufferElement DisableOutputBuffer { get { return (DisableOutputBufferElement)this["disableoutputbuffer"]; } set { this["disableoutputbuffer"] = value; } } [ConfigurationProperty("bootstrapper")] public BootstrapperElement Bootstrapper { get { return (BootstrapperElement)this["bootstrapper"]; } set { this["bootstrapper"] = value; } } public class DisableOutputBufferElement : ConfigurationElement { [ConfigurationProperty("value", DefaultValue = false, IsRequired = true)] public bool Value { get { return (bool)this["value"]; } set { this["value"] = value; } } } public class BootstrapperElement : ConfigurationElement { [ConfigurationProperty("type", DefaultValue = "", IsRequired = true)] public string Type { get { return (string)this["type"]; } set { this["type"] = value; } } [ConfigurationProperty("assembly", DefaultValue = "", IsRequired = true)] public string Assembly { get { return (String)this["assembly"]; } set { this["assembly"] = value; } } } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/NancyHandler.cs ================================================ namespace Nancy.Hosting.Aspnet { using System; using System.Collections.Generic; using System.Configuration; using System.Globalization; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using System.Web; using Nancy.Extensions; using Nancy.IO; /// /// Bridges the communication between Nancy and ASP.NET based hosting. /// public class NancyHandler { private readonly INancyEngine engine; /// /// Initializes a new instance of the type for the specified . /// /// An instance, that should be used by the handler. public NancyHandler(INancyEngine engine) { this.engine = engine; } /// /// Processes the ASP.NET request with Nancy. /// /// The of the request. public async Task ProcessRequest(HttpContextBase httpContext) { var request = CreateNancyRequest(httpContext); using(var nancyContext = await this.engine.HandleRequest(request).ConfigureAwait(false)) { SetNancyResponseToHttpResponse(httpContext, nancyContext.Response); } } private static Request CreateNancyRequest(HttpContextBase context) { var incomingHeaders = context.Request.Headers.ToDictionary(); var expectedRequestLength = GetExpectedRequestLength(incomingHeaders); var basePath = context.Request.ApplicationPath.TrimEnd('/'); var path = context.Request.Url.AbsolutePath.Substring(basePath.Length); path = string.IsNullOrWhiteSpace(path) ? "/" : path; var nancyUrl = new Url { Scheme = context.Request.Url.Scheme, HostName = context.Request.Url.Host, Port = context.Request.Url.Port, BasePath = basePath, Path = path, Query = context.Request.Url.Query, }; byte[] certificate = null; if (context.Request.ClientCertificate != null && context.Request.ClientCertificate.IsPresent && context.Request.ClientCertificate.Certificate.Length != 0) { certificate = context.Request.ClientCertificate.Certificate; } RequestStream body = null; if (expectedRequestLength != 0 || HasChunkedEncoding(incomingHeaders)) { body = RequestStream.FromStream(context.Request.InputStream, expectedRequestLength, StaticConfiguration.DisableRequestStreamSwitching ?? true); } var protocolVersion = context.Request.ServerVariables["HTTP_VERSION"]; return new Request(context.Request.HttpMethod.ToUpperInvariant(), nancyUrl, body, incomingHeaders, context.Request.UserHostAddress, new X509Certificate2(certificate), protocolVersion); } private static long GetExpectedRequestLength(IDictionary> incomingHeaders) { if (incomingHeaders == null) { return 0; } IEnumerable values; if (!incomingHeaders.TryGetValue("Content-Length", out values)) { return 0; } var headerValue = values.SingleOrDefault(); if (headerValue == null) { return 0; } long contentLength; if (!long.TryParse(headerValue, NumberStyles.Any, CultureInfo.InvariantCulture, out contentLength)) { return 0; } return contentLength; } private static bool HasChunkedEncoding(IDictionary> incomingHeaders) { IEnumerable transferEncodingValue; if (incomingHeaders == null || !incomingHeaders.TryGetValue("Transfer-Encoding", out transferEncodingValue)) { return false; } var transferEncodingString = transferEncodingValue.SingleOrDefault() ?? string.Empty; return transferEncodingString.Equals("chunked", StringComparison.OrdinalIgnoreCase); } public static void SetNancyResponseToHttpResponse(HttpContextBase context, Response response) { SetHttpResponseHeaders(context, response); if (response.ContentType != null) { context.Response.ContentType = response.ContentType; } if (IsOutputBufferDisabled()) { context.Response.BufferOutput = false; } context.Response.StatusCode = (int) response.StatusCode; if (response.ReasonPhrase != null) { context.Response.StatusDescription = response.ReasonPhrase; } response.Contents.Invoke(new NancyResponseStream(context.Response)); } private static bool IsOutputBufferDisabled() { var configurationSection = ConfigurationManager.GetSection("nancyFx") as NancyFxSection; if (configurationSection == null || configurationSection.DisableOutputBuffer == null) { return false; } return configurationSection.DisableOutputBuffer.Value; } private static void SetHttpResponseHeaders(HttpContextBase context, Response response) { foreach (var header in response.Headers.ToDictionary(x => x.Key, x => x.Value)) { context.Response.AddHeader(header.Key, header.Value); } foreach(var cookie in response.Cookies.ToArray()) { context.Response.AddHeader("Set-Cookie", cookie.ToString()); } } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/NancyHttpRequestHandler.cs ================================================ namespace Nancy.Hosting.Aspnet { using System; using System.Configuration; using System.Threading.Tasks; using System.Web; using Nancy.Bootstrapper; public class NancyHttpRequestHandler : HttpTaskAsyncHandler { private static readonly INancyEngine engine; static NancyHttpRequestHandler() { var bootstrapper = GetBootstrapper(); bootstrapper.Initialise(); engine = bootstrapper.GetEngine(); } public override bool IsReusable { get { return true; } } private static INancyBootstrapper GetBootstrapper() { return GetConfigurationBootstrapper() ?? NancyBootstrapperLocator.Bootstrapper; } private static INancyBootstrapper GetConfigurationBootstrapper() { var bootstrapperEntry = GetConfiguredBootstrapperEntry(); if (bootstrapperEntry == null) { return null; } var assemblyQualifiedName = string.Concat(bootstrapperEntry.Name, ", ", bootstrapperEntry.Assembly); var bootstrapperType = Type.GetType(assemblyQualifiedName); if (bootstrapperType == null) { throw new BootstrapperException(string.Format("Could not locate bootstrapper of type '{0}'.", assemblyQualifiedName)); } try { return Activator.CreateInstance(bootstrapperType) as INancyBootstrapper; } catch (Exception ex) { var errorMessage = string.Format("Could not initialize bootstrapper of type '{0}'.", bootstrapperType.FullName); throw new BootstrapperException(errorMessage, ex); } } private static BootstrapperEntry GetConfiguredBootstrapperEntry() { var configurationSection = ConfigurationManager.GetSection("nancyFx") as NancyFxSection; if (configurationSection == null) { return null; } var bootstrapperOverrideType = configurationSection.Bootstrapper.Type; var bootstrapperOverrideAssembly = configurationSection.Bootstrapper.Assembly; if (string.IsNullOrWhiteSpace(bootstrapperOverrideType)) { throw new BootstrapperException("The 'type' attribute of the 'bootstrapper' element under the 'nancyFx' config section is empty. Please specify the full type name."); } if (string.IsNullOrWhiteSpace(bootstrapperOverrideAssembly)) { throw new BootstrapperException("The 'assembly' attribute of the 'bootstrapper' element under the 'nancyFx' config section is empty. Please specify the full assembly name."); } return new BootstrapperEntry(bootstrapperOverrideAssembly, bootstrapperOverrideType); } public override Task ProcessRequestAsync(HttpContext context) { var nancyHandler = new NancyHandler(engine); return nancyHandler.ProcessRequest(new HttpContextWrapper(context)); } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/NancyResponseStream.cs ================================================ namespace Nancy.Hosting.Aspnet { using System.IO; using System.Web; /// /// A wrapper around an AspNet OutputStream that defers .Flush() to the parent HttpResponseBase /// public class NancyResponseStream : Stream { private readonly HttpResponseBase response; public NancyResponseStream(HttpResponseBase response) { this.response = response; } public override bool CanRead { get { return this.response.OutputStream.CanRead; } } public override bool CanSeek { get { return this.response.OutputStream.CanSeek; } } public override bool CanWrite { get { return this.response.OutputStream.CanWrite; } } /// /// Calls Flush() on the wrapped HttpResponseBase /// public override void Flush() { this.response.Flush(); } public override long Length { get { return this.response.OutputStream.Length; } } public override long Position { get { return this.response.OutputStream.Position; } set { this.response.OutputStream.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return this.response.OutputStream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return this.response.OutputStream.Seek(offset, origin); } public override void SetLength(long value) { this.response.OutputStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { this.response.OutputStream.Write(buffer, offset, count); } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/TinyIoCAspNetExtensions.cs ================================================ namespace Nancy.Hosting.Aspnet { using System; using System.Web; using Nancy.TinyIoc; public class HttpContextLifetimeProvider : TinyIoCContainer.ITinyIoCObjectLifetimeProvider { private readonly string _KeyName = String.Format("TinyIoC.HttpContext.{0}", Guid.NewGuid()); public object GetObject() { return HttpContext.Current.Items[_KeyName]; } public void SetObject(object value) { HttpContext.Current.Items[_KeyName] = value; } public void ReleaseObject() { var item = GetObject() as IDisposable; if (item != null) item.Dispose(); SetObject(null); } } public static class TinyIoCAspNetExtensions { /// /// Registers the dependency as per request lifetime /// /// Register options /// Register options public static TinyIoCContainer.RegisterOptions AsPerRequestSingleton(this TinyIoCContainer.RegisterOptions registerOptions) { return TinyIoCContainer.RegisterOptions.ToCustomLifetimeManager(registerOptions, new HttpContextLifetimeProvider(), "per request singleton"); } /// /// Registers each item in the collection as per request lifetime /// /// Register options /// Register options public static TinyIoCContainer.MultiRegisterOptions AsPerRequestSingleton(this TinyIoCContainer.MultiRegisterOptions registerOptions) { return TinyIoCContainer.MultiRegisterOptions.ToCustomLifetimeManager(registerOptions, new HttpContextLifetimeProvider(), "per request singleton"); } } } ================================================ FILE: src/Nancy.Hosting.Aspnet/web.config.transform ================================================ ================================================ FILE: src/Nancy.Hosting.Self/AutomaticUrlReservationCreationFailureException.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Collections.Generic; using System.Text; /// /// Exception for when automatic address reservation creation fails. /// Provides the user with manual instructions. /// public class AutomaticUrlReservationCreationFailureException : Exception { private readonly IEnumerable prefixes; private readonly string user; public AutomaticUrlReservationCreationFailureException(IEnumerable prefixes, string user) { this.prefixes = prefixes; this.user = user; } /// /// Gets a message that describes the current exception. /// /// /// The error message that explains the reason for the exception, or an empty string(""). /// /// 1 public override string Message { get { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine("The Nancy self host was unable to start, as no namespace reservation existed for the provided url(s)."); stringBuilder.AppendLine(); stringBuilder.AppendLine("Please either enable UrlReservations.CreateAutomatically on the HostConfiguration provided to "); stringBuilder.AppendLine("the NancyHost, or create the reservations manually with the (elevated) command(s):"); stringBuilder.AppendLine(); foreach (var prefix in prefixes) { var command = NetSh.GetParameters(prefix, user); stringBuilder.AppendLine(string.Format("netsh {0}", command)); } return stringBuilder.ToString(); } } } } ================================================ FILE: src/Nancy.Hosting.Self/FileSystemRootPathProvider.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.IO; using System.Reflection; public class FileSystemRootPathProvider : IRootPathProvider { private readonly Lazy rootPath = new Lazy(ExtractRootPath); public string GetRootPath() { return this.rootPath.Value; } private static string ExtractRootPath() { var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var location = assembly.Location; return Path.GetDirectoryName(location); } } } ================================================ FILE: src/Nancy.Hosting.Self/HostConfiguration.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Diagnostics; /// /// Host configuration for the self host. /// public sealed class HostConfiguration { /// /// Gets or sets a property that determines if localhost URIs are /// rewritten to http://+:port/ style URIs to allow for listening on /// all IP addresses, but requiring either a URL reservation, or admin /// access. /// Defaults to true. /// public bool RewriteLocalhost { get; set; } /// /// Configuration around automatically creating URL reservations. /// public UrlReservations UrlReservations { get; set; } /// /// Gets or sets a property that determines if Transfer-Encoding: Chunked is allowed /// for the response instead of Content-Length. /// Defaults to true. /// public bool AllowChunkedEncoding { get; set; } /// /// Gets or sets a property that provides a callback to be called /// if there is an unhandled exception in the self host. /// Note: this will *not* be called for normal Nancy exceptions /// that are handled by the Nancy handlers. /// Defaults to writing to debug out. /// public Action UnhandledExceptionCallback { get; set; } /// /// Gets or sets a property that determines whether client certificates /// are enabled or not. /// When set to true the self host will request a client certificate if the /// request is running over SSL. /// Defaults to false. /// public bool EnableClientCertificates { get; set; } /// /// Gets or sets a property determining if base URI matching can fall back to just /// using Authority (Schema + Host + Port) as base URI if it cannot match anything in /// the known list. This should only be set to True if you have issues with port forwarding /// (e.g. on Azure). /// Defaults to false. /// public bool AllowAuthorityFallback { get; set; } /// /// Gets or sets a property determining how many total connections the NancyHost can maintain simultaneously. /// Higher values mean more connections can be maintained at a slower average response times; while fewer /// connections will be rejected. /// Lower values will result in fewer conections, yet will be maintained at a faster average response time. /// Defaults to the approximate number of processor threads. /// public int MaximumConnectionCount { get; set; } /// /// Gets approximate processor thread count by halfing the Logical Core count to /// account for hyper-threading. /// private static int ProcessorThreadCount { get { // Divide by 2 for hyper-threading, and good defaults. var threadCount = Environment.ProcessorCount >> 1; if (threadCount < 1) { // Ensure thread count is at least 1. return 1; } return threadCount; } } /// /// Initializes the default configuration. /// MaximumConnectionCount by default is half of the Logical Core count. /// /// /// If the system running NancyHost is not using hyper-threading you may want to consider /// supplying your own values for MaximumConnectionCount as the default assumes /// hyperthreading is being utilized. /// public HostConfiguration() { this.RewriteLocalhost = true; this.UrlReservations = new UrlReservations(); this.AllowChunkedEncoding = true; this.UnhandledExceptionCallback = e => { var message = string.Format("---\n{0}\n---\n", e); Debug.Write(message); }; this.EnableClientCertificates = false; this.MaximumConnectionCount = ProcessorThreadCount; } } } ================================================ FILE: src/Nancy.Hosting.Self/IgnoredHeaders.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Collections.Generic; /// /// A helper class that checks for a header against a list of headers that should be ignored /// when populating the headers of an object. /// public static class IgnoredHeaders { private static readonly HashSet knownHeaders = new HashSet(StringComparer.OrdinalIgnoreCase) { "content-length", "content-type", "transfer-encoding", "keep-alive" }; /// /// Determines if a header is ignored when populating the headers of an /// object. /// /// The name of the header. /// true if the header is ignored; otherwise, false. public static bool IsIgnored(string headerName) { return knownHeaders.Contains(headerName); } } } ================================================ FILE: src/Nancy.Hosting.Self/Nancy.Hosting.Self.csproj ================================================  Enables hosting Nancy in any application. $(PackageTags);Host netstandard2.0;net452 4.3.0 ================================================ FILE: src/Nancy.Hosting.Self/NancyHost.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using System.Threading.Tasks; using Nancy.Bootstrapper; using Nancy.Extensions; using Nancy.IO; using System.Threading; /// /// Allows to host Nancy server inside any application - console or windows service. /// /// /// NancyHost uses internally. Therefore, it requires full .net 4.0 profile (not client profile) /// to run. will launch a thread that will listen for requests and then process them. Each request is processed in /// its own execution thread. NancyHost needs in order to be used from another appdomain under /// mono. Working with AppDomains is necessary if you want to unload the dependencies that come with NancyHost. /// [Serializable] public class NancyHost : IDisposable { private const int ACCESS_DENIED = 5; private readonly IList baseUriList; private HttpListener listener; private readonly INancyEngine engine; private readonly HostConfiguration configuration; private readonly INancyBootstrapper bootstrapper; private bool stop = false; /// /// Initializes a new instance of the class for the specified . /// Uses the default configuration /// /// The s that the host will listen to. public NancyHost(params Uri[] baseUris) : this(NancyBootstrapperLocator.Bootstrapper, new HostConfiguration(), baseUris) { } /// /// Initializes a new instance of the class for the specified . /// Uses the specified configuration. /// /// The s that the host will listen to. /// Configuration to use public NancyHost(HostConfiguration configuration, params Uri[] baseUris) : this(NancyBootstrapperLocator.Bootstrapper, configuration, baseUris){} /// /// Initializes a new instance of the class for the specified , using /// the provided . /// Uses the default configuration /// /// The bootstrapper that should be used to handle the request. /// The s that the host will listen to. public NancyHost(INancyBootstrapper bootstrapper, params Uri[] baseUris) : this(bootstrapper, new HostConfiguration(), baseUris) { } /// /// Initializes a new instance of the class for the specified , using /// the provided . /// Uses the specified configuration. /// /// The bootstrapper that should be used to handle the request. /// Configuration to use /// The s that the host will listen to. public NancyHost(INancyBootstrapper bootstrapper, HostConfiguration configuration, params Uri[] baseUris) { this.bootstrapper = bootstrapper; this.configuration = configuration ?? new HostConfiguration(); this.baseUriList = baseUris; bootstrapper.Initialise(); this.engine = bootstrapper.GetEngine(); } /// /// Initializes a new instance of the class for the specified , using /// the provided . /// Uses the default configuration /// /// The that the host will listen to. /// The bootstrapper that should be used to handle the request. public NancyHost(Uri baseUri, INancyBootstrapper bootstrapper) : this(bootstrapper, new HostConfiguration(), baseUri) { } /// /// Initializes a new instance of the class for the specified , using /// the provided . /// Uses the specified configuration. /// /// The that the host will listen to. /// The bootstrapper that should be used to handle the request. /// Configuration to use public NancyHost(Uri baseUri, INancyBootstrapper bootstrapper, HostConfiguration configuration) : this (bootstrapper, configuration, baseUri) { } /// /// Stops the host if it is running. /// public void Dispose() { this.Stop(); this.bootstrapper.Dispose(); } /// /// Start listening for incoming requests with the given configuration /// public void Start() { this.StartListener(); Task.Run(() => { var semaphore = new Semaphore(this.configuration.MaximumConnectionCount, this.configuration.MaximumConnectionCount); while (!this.stop) { semaphore.WaitOne(); this.listener.GetContextAsync().ContinueWith(async (contextTask) => { try { semaphore.Release(); var context = await contextTask.ConfigureAwait(false); await this.Process(context).ConfigureAwait(false); } catch (Exception ex) { this.configuration.UnhandledExceptionCallback.Invoke(ex); throw; } }); } }); } private void StartListener() { if (this.TryStartListener()) { return; } if (!this.configuration.UrlReservations.CreateAutomatically) { throw new AutomaticUrlReservationCreationFailureException(this.GetPrefixes(), this.GetUser()); } if (!this.TryAddUrlReservations()) { throw new InvalidOperationException("Unable to configure namespace reservation"); } if (!this.TryStartListener()) { throw new InvalidOperationException("Unable to start listener"); } } private bool TryStartListener() { try { // if the listener fails to start, it gets disposed; // so we need a new one, each time. this.listener = new HttpListener(); foreach (var prefix in this.GetPrefixes()) { this.listener.Prefixes.Add(prefix); } this.listener.Start(); return true; } catch (HttpListenerException e) { if (e.ErrorCode == ACCESS_DENIED) { return false; } throw; } } private bool TryAddUrlReservations() { var user = this.GetUser(); foreach (var prefix in this.GetPrefixes()) { if (!NetSh.AddUrlAcl(prefix, user)) { return false; } } return true; } private string GetUser() { return !string.IsNullOrWhiteSpace(this.configuration.UrlReservations.User) ? this.configuration.UrlReservations.User : WindowsIdentity.GetCurrent().Name; } /// /// Stop listening for incoming requests. /// public void Stop() { if (this.listener != null && this.listener.IsListening) { this.stop = true; this.listener.Stop(); } } internal IEnumerable GetPrefixes() { foreach (var baseUri in this.baseUriList) { var prefix = new UriBuilder(baseUri).ToString(); if (this.configuration.RewriteLocalhost && !baseUri.Host.Contains(".")) { prefix = prefix.Replace("localhost", "+"); } yield return prefix; } } private Request ConvertRequestToNancyRequest(HttpListenerRequest request) { var baseUri = this.GetBaseUri(request); if (baseUri == null) { throw new InvalidOperationException(string.Format("Unable to locate base URI for request: {0}",request.Url)); } var expectedRequestLength = GetExpectedRequestLength(request.Headers.ToDictionary()); var nancyUrl = new Url { Scheme = request.Url.Scheme, HostName = request.Url.Host, Port = request.Url.IsDefaultPort ? null : (int?)request.Url.Port, BasePath = baseUri.AbsolutePath.TrimEnd('/'), Path = baseUri.MakeAppLocalPath(request.Url), Query = request.Url.Query }; X509Certificate2 certificate = null; if (this.configuration.EnableClientCertificates) { var x509Certificate = request.GetClientCertificate(); if (x509Certificate != null) { certificate = x509Certificate; } } // NOTE: For HTTP/2 we want fieldCount = 1, // otherwise (HTTP/1.0 and HTTP/1.1) we want fieldCount = 2 var fieldCount = request.ProtocolVersion.Major == 2 ? 1 : 2; var protocolVersion = string.Format("HTTP/{0}", request.ProtocolVersion.ToString(fieldCount)); return new Request( request.HttpMethod, nancyUrl, RequestStream.FromStream(request.InputStream, expectedRequestLength, StaticConfiguration.DisableRequestStreamSwitching ?? false), request.Headers.ToDictionary(), (request.RemoteEndPoint != null) ? request.RemoteEndPoint.Address.ToString() : null, certificate, protocolVersion); } private Uri GetBaseUri(HttpListenerRequest request) { var result = this.baseUriList.FirstOrDefault(uri => uri.IsCaseInsensitiveBaseOf(request.Url)); if (result != null) { return result; } if (!this.configuration.AllowAuthorityFallback) { return null; } return new Uri(request.Url.GetLeftPart(UriPartial.Authority)); } private void ConvertNancyResponseToResponse(Response nancyResponse, HttpListenerResponse response) { foreach (var header in nancyResponse.Headers) { if (!IgnoredHeaders.IsIgnored(header.Key)) { response.AddHeader(header.Key, header.Value); } } foreach (var nancyCookie in nancyResponse.Cookies) { response.Headers.Add(HttpResponseHeader.SetCookie, nancyCookie.ToString()); } if (nancyResponse.ReasonPhrase != null) { response.StatusDescription = nancyResponse.ReasonPhrase; } if (nancyResponse.ContentType != null) { response.ContentType = nancyResponse.ContentType; } response.StatusCode = (int)nancyResponse.StatusCode; if (this.configuration.AllowChunkedEncoding) { OutputWithDefaultTransferEncoding(nancyResponse, response); } else { OutputWithContentLength(nancyResponse, response); } } private static void OutputWithDefaultTransferEncoding(Response nancyResponse, HttpListenerResponse response) { using (var output = response.OutputStream) { nancyResponse.Contents.Invoke(output); } } private static void OutputWithContentLength(Response nancyResponse, HttpListenerResponse response) { byte[] buffer; using (var memoryStream = new MemoryStream()) { nancyResponse.Contents.Invoke(memoryStream); buffer = memoryStream.ToArray(); } string value; var contentLength = nancyResponse.Headers.TryGetValue("Content-Length", out value) ? Convert.ToInt64(value) : buffer.Length; response.SendChunked = false; response.ContentLength64 = contentLength; using (var output = response.OutputStream) { using (var writer = new BinaryWriter(output)) { writer.Write(buffer); writer.Flush(); } } } private static long GetExpectedRequestLength(IDictionary> incomingHeaders) { if (incomingHeaders == null) { return 0; } IEnumerable values; if (!incomingHeaders.TryGetValue("Content-Length", out values)) { return 0; } var headerValue = values.SingleOrDefault(); if (headerValue == null) { return 0; } long contentLength; return !long.TryParse(headerValue, NumberStyles.Any, CultureInfo.InvariantCulture, out contentLength) ? 0 : contentLength; } private async Task Process(HttpListenerContext ctx) { try { var nancyRequest = this.ConvertRequestToNancyRequest(ctx.Request); using (var nancyContext = await this.engine.HandleRequest(nancyRequest).ConfigureAwait(false)) { try { this.ConvertNancyResponseToResponse(nancyContext.Response, ctx.Response); } catch (Exception e) { this.configuration.UnhandledExceptionCallback.Invoke(e); } } } catch (Exception e) { this.configuration.UnhandledExceptionCallback.Invoke(e); } } } } ================================================ FILE: src/Nancy.Hosting.Self/NetSh.cs ================================================ namespace Nancy.Hosting.Self { using System; /// /// Executes NetSh commands /// public static class NetSh { private const string NetshCommand = "netsh"; /// /// Add a url reservation /// /// Url to add /// User to add the reservation for /// True if successful, false otherwise. public static bool AddUrlAcl(string url, string user) { try { var arguments = GetParameters(url, user); return UacHelper.RunElevated(NetshCommand, arguments); } catch (Exception) { return false; } } internal static string GetParameters(string url, string user) { return string.Format("http add urlacl url=\"{0}\" user=\"{1}\"", url, user); } } } ================================================ FILE: src/Nancy.Hosting.Self/Properties/InternalsVisibleTo.cs ================================================ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Nancy.Hosting.Self.Tests")] ================================================ FILE: src/Nancy.Hosting.Self/UacHelper.cs ================================================ namespace Nancy.Hosting.Self { using System.Diagnostics; /// /// Helpers for UAC /// public static class UacHelper { /// /// Run an executable elevated /// /// File to execute /// Arguments to pass to the executable /// True if successful, false otherwise public static bool RunElevated(string file, string args) { var process = CreateProcess(args, file); process.Start(); process.WaitForExit(); return process.ExitCode == 0; } private static Process CreateProcess(string args, string file) { return new Process { StartInfo = new ProcessStartInfo { Verb = "runas", Arguments = args, FileName = file, } }; } } } ================================================ FILE: src/Nancy.Hosting.Self/UriExtensions.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Collections.Generic; using System.Text; /// /// Extension methods for working with instances. /// public static class UriExtensions { public static bool IsCaseInsensitiveBaseOf(this Uri source, Uri value) { var uriComponents = source.Host == "localhost" ? (UriComponents.Port | UriComponents.Scheme) : (UriComponents.HostAndPort | UriComponents.Scheme); if (Uri.Compare(source, value, uriComponents, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) != 0) { return false; } var sourceSegments = source.Segments; var valueSegments = value.Segments; return sourceSegments.ZipCompare(valueSegments, (s1, s2) => s1.Length == 0 || SegmentEquals(s1, s2)); } public static string MakeAppLocalPath(this Uri appBaseUri, Uri fullUri) { return string.Concat("/", appBaseUri.Segments.ZipFill(fullUri.Segments, (x, y) => x != null && SegmentEquals(x, y) ? null : y).Join()); } private static string AppendSlashIfNeeded(string segment) { if (!segment.EndsWith("/")) { segment = string.Concat(segment, "/"); } return segment; } private static bool SegmentEquals(string segment1, string segment2) { return String.Equals(AppendSlashIfNeeded(segment1), AppendSlashIfNeeded(segment2), StringComparison.OrdinalIgnoreCase); } private static bool ZipCompare(this IEnumerable source1, IEnumerable source2, Func comparison) { using (var enumerator1 = source1.GetEnumerator()) { using (var enumerator2 = source2.GetEnumerator()) { var has1 = enumerator1.MoveNext(); var has2 = enumerator2.MoveNext(); while (has1 || has2) { var current1 = has1 ? enumerator1.Current : ""; var current2 = has2 ? enumerator2.Current : ""; if (!comparison(current1, current2)) { return false; } if (has1) { has1 = enumerator1.MoveNext(); } if (has2) { has2 = enumerator2.MoveNext(); } } } } return true; } private static IEnumerable ZipFill(this IEnumerable source1, IEnumerable source2, Func selector) { using (var enumerator1 = source1.GetEnumerator()) { using (var enumerator2 = source2.GetEnumerator()) { var has1 = enumerator1.MoveNext(); var has2 = enumerator2.MoveNext(); while (has1 || has2) { var value1 = has1 ? enumerator1.Current : null; var value2 = has2 ? enumerator2.Current : null; var value = selector(value1, value2); if (value != null) { yield return value; } if (has1) { has1 = enumerator1.MoveNext(); } if (has2) { has2 = enumerator2.MoveNext(); } } } } } private static string Join(this IEnumerable source) { var builder = new StringBuilder(); foreach (var value in source) { builder.Append(value); } return builder.ToString(); } } } ================================================ FILE: src/Nancy.Hosting.Self/UrlReservations.cs ================================================ namespace Nancy.Hosting.Self { using System; using System.Security.Principal; /// /// Configuration for automatic url reservation creation /// public class UrlReservations { private const string EveryoneAccountName = "Everyone"; private static readonly IdentityReference EveryoneReference = new SecurityIdentifier(WellKnownSidType.WorldSid, null); public UrlReservations() { this.CreateAutomatically = false; this.User = GetEveryoneAccountName(); } /// /// Gets or sets a value indicating whether url reservations /// are automatically created when necessary. /// Defaults to false. /// public bool CreateAutomatically { get; set; } /// /// Gets or sets a value for the user to use to create the url reservations for. /// Defaults to the "Everyone" group. /// public string User { get; set; } private static string GetEveryoneAccountName() { try { var account = EveryoneReference.Translate(typeof(NTAccount)) as NTAccount; if (account != null) { return account.Value; } return EveryoneAccountName; } catch (Exception) { return EveryoneAccountName; } } } } ================================================ FILE: src/Nancy.Metadata.Modules/DefaultMetadataModuleConventions.cs ================================================ namespace Nancy.Metadata.Modules { using System; using System.Collections; using System.Collections.Generic; using System.Linq; /// /// This is a wrapper around the type /// 'IEnumerable{Func{INancyModule, IEnumerable{IMetadataModule}, IMetadataModule}}' and its /// only purpose is to make Ninject happy which was throwing an exception /// when constructor injecting this type. /// public class DefaultMetadataModuleConventions : IEnumerable, IMetadataModule>> { private readonly IEnumerable, IMetadataModule>> conventions; /// /// Initializes a new instance of the class. /// public DefaultMetadataModuleConventions() { this.conventions = this.ConfigureMetadataModuleConventions(); } public IEnumerator, IMetadataModule>> GetEnumerator() { return this.conventions.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } private static string ReplaceModuleWithMetadataModule(string moduleName) { var i = moduleName.LastIndexOf("Module"); return moduleName.Substring(0, i) + "MetadataModule"; } private IEnumerable, IMetadataModule>> ConfigureMetadataModuleConventions() { return new List, IMetadataModule>> { // 0 Handles: ./BlahModule -> ./BlahMetadataModule (module, metadataModules) => { var moduleType = module.GetType(); var moduleName = moduleType.FullName; var metadataModuleName = ReplaceModuleWithMetadataModule(moduleName); return metadataModules.FirstOrDefault(m => string.Compare(m.GetType().FullName, metadataModuleName, StringComparison.OrdinalIgnoreCase) == 0); }, // 1 Handles: ./BlahModule -> ./Metadata/BlahMetadataModule (module, metadataModules) => { var moduleType = module.GetType(); var moduleName = moduleType.FullName; var parts = moduleName.Split('.').ToList(); parts.Insert(parts.Count - 1, "Metadata"); var metadataModuleName = ReplaceModuleWithMetadataModule(string.Join(".", (IEnumerable)parts)); return metadataModules.FirstOrDefault(m => string.Compare(m.GetType().FullName, metadataModuleName, StringComparison.OrdinalIgnoreCase) == 0); }, // 2 Handles: ./Modules/BlahModule -> ../Metadata/BlahMetadataModule (module, metadataModules) => { var moduleType = module.GetType(); var moduleName = moduleType.FullName; var parts = moduleName.Split('.').ToList(); parts[parts.Count - 2] = "Metadata"; var metadataModuleName = ReplaceModuleWithMetadataModule(string.Join(".", (IEnumerable)parts)); return metadataModules.FirstOrDefault(m => string.Compare(m.GetType().FullName, metadataModuleName, StringComparison.OrdinalIgnoreCase) == 0); } }; } } } ================================================ FILE: src/Nancy.Metadata.Modules/DefaultMetadataModuleResolver.cs ================================================ namespace Nancy.Metadata.Modules { using System; using System.Collections.Generic; using System.Linq; /// /// Default implementation on how metadata modules are resolved by Nancy. /// public class DefaultMetadataModuleResolver : IMetadataModuleResolver { private readonly DefaultMetadataModuleConventions conventions; private readonly IEnumerable metadataModules; /// /// Initializes a new instance of the class. /// /// The conventions that the resolver should use to determine which metadata module to return. /// The metadata modules to use resolve against. public DefaultMetadataModuleResolver(DefaultMetadataModuleConventions conventions, IEnumerable metadataModules) { if (conventions == null) { throw new InvalidOperationException("Cannot create an instance of DefaultMetadataModuleResolver with conventions parameter having null value."); } if (metadataModules == null) { throw new InvalidOperationException("Cannot create an instance of DefaultMetadataModuleResolver with metadataModules parameter having null value."); } this.conventions = conventions; this.metadataModules = metadataModules; } /// /// Resolves a metadata module instance based on the provided information. /// /// The . /// An instance if one could be found, otherwise . public IMetadataModule GetMetadataModule(INancyModule module) { return this.conventions .Select(convention => this.SafeInvokeConvention(convention, module)) .FirstOrDefault(metadataModule => metadataModule != null); } private IMetadataModule SafeInvokeConvention(Func, IMetadataModule> convention, INancyModule module) { try { return convention.Invoke(module, this.metadataModules); } catch { return null; } } } } ================================================ FILE: src/Nancy.Metadata.Modules/IMetadataModule.cs ================================================ namespace Nancy.Metadata.Modules { using System; using Nancy.Routing; /// /// Defines facilities for obtaining metadata for a given . /// public interface IMetadataModule { /// /// Gets the of metadata the returns. /// Type MetadataType { get; } /// /// Returns metadata for the given . /// /// The route to obtain metadata for. /// An instance of if one exists, otherwise null. object GetMetadata(RouteDescription description); } } ================================================ FILE: src/Nancy.Metadata.Modules/IMetadataModuleResolver.cs ================================================ namespace Nancy.Metadata.Modules { /// /// Defines the functionality for resolving the metadata module for a given . /// public interface IMetadataModuleResolver { /// /// Resolves a metadata module instance based on the provided information. /// /// The . /// An instance if one could be found, otherwise . IMetadataModule GetMetadataModule(INancyModule module); } } ================================================ FILE: src/Nancy.Metadata.Modules/MetadataModule.cs ================================================ namespace Nancy.Metadata.Modules { using System; using System.Collections.Generic; using Nancy.Routing; /// /// Base class containing the functionality for obtaining metadata for a given . /// public abstract class MetadataModule : IMetadataModule where TMetadata : class { private readonly IDictionary> metadata; protected MetadataModule() { this.metadata = new Dictionary>(); } /// /// Gets for describing routes. /// /// A instance. public RouteMetadataBuilder Describe { get { return new RouteMetadataBuilder(this); } } /// /// Gets the of metadata based on . /// public Type MetadataType { get { return typeof(TMetadata); } } /// /// Returns metadata for the given . /// /// The route to obtain metadata for. /// An instance of if one exists, otherwise null. public object GetMetadata(RouteDescription description) { Func func; if (this.metadata.TryGetValue(description.Name, out func)) { return func.Invoke(description); } return null; } /// /// Helper class for configuring a route metadata handler in a module. /// public class RouteMetadataBuilder { private readonly MetadataModule parentModule; /// /// Initializes a new instance of the class. /// /// The that the route is being configured for. public RouteMetadataBuilder(MetadataModule metadataModule) { this.parentModule = metadataModule; } /// /// Describes metadata for a route with the specified . /// /// A delegate that is used to return the route metadata. public Func this[string name] { set { this.AddRouteMetadata(name, value); } } protected void AddRouteMetadata(string name, Func value) { this.parentModule.metadata.Add(name, value); } } } } ================================================ FILE: src/Nancy.Metadata.Modules/MetadataModuleRegistrations.cs ================================================ namespace Nancy.Metadata.Modules { using Nancy.Bootstrapper; /// /// Performs application registations for metadata modules. /// public class MetadataModuleRegistrations : Registrations { /// /// Creates a new instance of the class, that performs /// the default registrations of the metadata modules types. /// /// An instance. public MetadataModuleRegistrations(ITypeCatalog typeCatalog) : base(typeCatalog) { this.Register(); this.RegisterAll(); this.RegisterWithDefault(typeof(DefaultMetadataModuleResolver)); } } } ================================================ FILE: src/Nancy.Metadata.Modules/MetadataModuleRouteMetadataProvider.cs ================================================ namespace Nancy.Metadata.Modules { using System; using Nancy.Routing; /// /// Provides metadata for routes by obtaining it from instances associated with . /// public class MetadataModuleRouteMetadataProvider : IRouteMetadataProvider { private readonly IMetadataModuleResolver resolver; /// /// Initializes a new instance of the class. /// /// Resolves instances. public MetadataModuleRouteMetadataProvider(IMetadataModuleResolver resolver) { this.resolver = resolver; } /// /// Gets the of the metadata that is created by the provider. /// /// The instance that the route is declared in. /// A for the route. /// A instance, or null if none are found. public Type GetMetadataType(INancyModule module, RouteDescription routeDescription) { var metadataModule = this.resolver.GetMetadataModule(module); return metadataModule != null ? metadataModule.MetadataType : null; } /// /// Gets the metadata for the provided route by obtaining it from an associated . /// /// The instance that the route is declared in. /// A for the route. /// An object representing the metadata for the given route, or null if none are found. public object GetMetadata(INancyModule module, RouteDescription routeDescription) { var metadataModule = this.resolver.GetMetadataModule(module); return metadataModule != null ? metadataModule.GetMetadata(routeDescription) : null; } } } ================================================ FILE: src/Nancy.Metadata.Modules/Nancy.Metadata.Modules.csproj ================================================  Nancy metadata modules to describe your APIs. $(PackageTags);Metadata netstandard2.0;net452 ================================================ FILE: src/Nancy.Owin/AppBuilderExtensions.cs ================================================ // ReSharper disable CheckNamespace namespace Owin // ReSharper restore CheckNamespace { using System; using System.Threading; using Nancy.Owin; /// /// OWIN extensions for Nancy /// public static class AppBuilderExtensions { private const string AppDisposingKey = "host.OnAppDisposing"; /// /// Adds Nancy to the OWIN pipeline. /// /// The application builder. /// The Nancy options. /// IAppBuilder. public static IAppBuilder UseNancy(this IAppBuilder builder, NancyOptions options = null) { var nancyOptions = options ?? new NancyOptions(); HookDisposal(builder, nancyOptions); return builder.Use(NancyMiddleware.UseNancy(nancyOptions)); } /// /// Adds Nancy to the OWIN pipeline. /// /// The application builder. /// A configuration builder action. /// IAppBuilder. public static IAppBuilder UseNancy(this IAppBuilder builder, Action configuration) { var options = new NancyOptions(); configuration(options); return UseNancy(builder, options); } private static void HookDisposal(IAppBuilder builder, NancyOptions nancyOptions) { object value; if (!builder.Properties.TryGetValue(AppDisposingKey, out value)) { return; } var appDisposing = value as CancellationToken?; if (appDisposing.HasValue) { appDisposing.Value.Register(nancyOptions.Bootstrapper.Dispose); } } } } ================================================ FILE: src/Nancy.Owin/Nancy.Owin.csproj ================================================  Nancy extensions for OWIN hosting. $(PackageTags);OWIN net452 ================================================ FILE: src/Nancy.Testing/Accept.cs ================================================ namespace Nancy.Testing { /// /// Constants for accept header media ranges /// public static class Accept { /// /// Json media range /// /// application/json public const string Json = "application/json"; /// /// Html media range /// /// text/html public const string Html = "text/html"; /// /// Text media range /// /// >text/plain public const string Text = "text/plain"; /// /// Xml media range /// /// application/xml public const string Xml = "application/xml"; } } ================================================ FILE: src/Nancy.Testing/AndConnector.cs ================================================ namespace Nancy.Testing { public class AndConnector : IHideObjectMembers { private TSource source; /// /// Initializes a new instance of the class. /// /// /// Source object /// public AndConnector(TSource source) { this.source = source; } /// /// And /// public TSource And { get { return this.source; } } } } ================================================ FILE: src/Nancy.Testing/AssertEqualityComparer.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.Reflection; public class AssertEqualityComparer : IEqualityComparer { private static bool IsTypeNullable(Type type) { return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } public bool Equals(T expected, T actual) { var type = typeof(T); if (!type.GetTypeInfo().IsValueType || IsTypeNullable(type)) { var actualIsNull = (Object.Equals(actual, default(T))); var expectedIsNull = (Object.Equals(expected, default(T))); if (actualIsNull || expectedIsNull) { return false; } } var equality = actual as IEquatable; if (equality != null) { return equality.Equals(expected); } var genericComparable = actual as IComparable; if (genericComparable != null) { return genericComparable.CompareTo(expected) == 0; } var comparable = actual as IComparable; if (comparable != null) { return comparable.CompareTo(expected) == 0; } return false; } public int GetHashCode(T actual) { throw new NotSupportedException(); } } } ================================================ FILE: src/Nancy.Testing/AssertException.cs ================================================ namespace Nancy.Testing { using System; using System.Runtime.Serialization; /// /// Exception that is thrown by assert extensions. /// public class AssertException : Exception { /// /// Initializes a new instance of the class. /// public AssertException() { } /// /// Initializes a new instance of the class. /// /// The exception message. public AssertException(string message) : base(message) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The inner exception. public AssertException(string message, Exception innerException) : base(message, innerException) { } #if !NETSTANDARD1_6 /// /// Initializes a new instance of the class. /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. /// The parameter is null. /// The class name is null or is zero (0). protected AssertException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } } ================================================ FILE: src/Nancy.Testing/AssertExtensions.cs ================================================ namespace Nancy.Testing { using System; using System.Linq; /// /// Defines assert extensions for HTML validation. /// public static class AssertExtensions { /// /// Asserts that an element should exist at least once /// public static AndConnector ShouldExist(this NodeWrapper node) { Asserts.NotNull(node); return new AndConnector(node); } /// /// Asserts that an element should exist at least once /// public static AndConnector ShouldExist(this QueryWrapper query) { if (!query.Any()) { throw new AssertException("The selector did not match any elements in the document."); } return new AndConnector(query); } /// /// Asserts that an element does not exist /// public static AndConnector ShouldNotExist(this QueryWrapper query) { if (query.Any()) { var message = string.Format("The selector matched {0} element(s) in the document.", query.Count()); throw new AssertException(message); } return new AndConnector(query); } /// /// Asserts that an element or element should exist one, and only once /// public static AndConnector ShouldExistOnce(this QueryWrapper query) { return new AndConnector(Asserts.Single(query)); } /// /// Asserts that an element or element should exist exactly the specified number of times /// The expected number of times the element should exist /// public static AndConnector ShouldExistExactly(this QueryWrapper query, int expectedNumberOfOccurrences) { var nodeWrappers = Asserts.Exactly(query, expectedNumberOfOccurrences); return new AndConnector(nodeWrappers as QueryWrapper); } /// /// Asserts that an element should be of a specific class /// public static AndConnector ShouldBeOfClass(this NodeWrapper node, string className) { Asserts.Equal(className, node.Attributes["class"]); return new AndConnector(node); } /// /// Asserts that all elements should be of a specific class /// public static AndConnector ShouldBeOfClass(this QueryWrapper query, string className) { query.ShouldExist(); foreach (var node in query) { node.ShouldBeOfClass(className); } return new AndConnector(query); } /// /// Asserts that a node contains the specified text /// public static AndConnector ShouldContain(this NodeWrapper node, string contents, StringComparison comparisonType = StringComparison.Ordinal) { Asserts.Contains(contents, node.InnerText, comparisonType); return new AndConnector(node); } /// /// Asserts that every node contains the specified text /// public static AndConnector AllShouldContain(this QueryWrapper query, string contents, StringComparison comparisonType = StringComparison.Ordinal) { query.ShouldExist(); Asserts.All(contents, query.Select(x => x.InnerText), x => x.IndexOf(contents, comparisonType) >= 0); return new AndConnector(query); } /// /// Asserts that any node contains the specified text /// public static AndConnector AnyShouldContain(this QueryWrapper query, string contents, StringComparison comparisonType = StringComparison.Ordinal) { query.ShouldExist(); Asserts.Any(contents, query.Select(x => x.InnerText), x => x.IndexOf(contents, comparisonType) >= 0); return new AndConnector(query); } /// /// Asserts that an element has a specific attribute /// public static AndConnector ShouldContainAttribute(this NodeWrapper node, string name) { Asserts.True(node.HasAttribute(name)); return new AndConnector(node); } /// /// Asserts that an element has a specific attribute with a specified value /// public static AndConnector ShouldContainAttribute(this NodeWrapper node, string name, string value, StringComparison comparisonType = StringComparison.Ordinal) { Asserts.Equal(value, node.Attributes[name], comparisonType); return new AndConnector(node); } /// /// Asserts that an element has a specific attribute /// public static AndConnector ShouldContainAttribute(this QueryWrapper query, string name) { query.ShouldExist(); foreach (var node in query) { node.ShouldContainAttribute(name); } return new AndConnector(query); } /// /// Asserts that an element has a specific attribute with a specified value /// public static AndConnector ShouldContainAttribute(this QueryWrapper query, string name, string value, StringComparison comparisonType = StringComparison.Ordinal) { query.ShouldExist(); foreach (var node in query) { node.ShouldContainAttribute(name, value); } return new AndConnector(query); } } } ================================================ FILE: src/Nancy.Testing/Asserts.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.Linq; /// /// Contains method for verifying the results of a test. /// public static class Asserts { public static void Contains(T expected, IEnumerable actual, IEqualityComparer comparer = null) { comparer = comparer ?? new AssertEqualityComparer(); Any(expected, actual, value => comparer.Equals(expected, value)); } public static void Any(T expected, IEnumerable actual, Func comparer) { if (actual != null) { if (actual.Any(comparer)) { return; } } throw new AssertException("The expected value was not found in the collection."); } public static void All(T expected, IEnumerable actual, Func comparer) { if (actual != null) { if (actual.All(comparer)) { return; } } throw new AssertException("All elements in the collection did not contain the expected value."); } public static void Contains(string expected, string actual, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) { if (expected == null || actual.IndexOf(expected, comparisonType) < 0) { throw new AssertException(string.Format("The expected value '{0}' was not a sub-string of the actual value '{1}'.", expected, actual)); } } public static void Equal(T expected, T actual) { var comparer = new AssertEqualityComparer(); if (!comparer.Equals(actual, expected)) { throw new AssertException(string.Format("The expected value '{0}' was not equal to the actual value '{1}'.", expected, actual)); } } public static void Equal(string expected, string actual, StringComparison comparisonType = StringComparison.Ordinal) { if (!String.Equals(expected, actual, comparisonType)) { throw new AssertException(string.Format("The expected value '{0}' was not equal to the actual value '{1}'.", expected, actual)); } } public static void False(bool condition) { if (condition) { throw new AssertException("The condition was not false."); } } public static void NotNull(object actual) { if (actual == null) { throw new AssertException("The value was null."); } } public static void Null(object actual) { if (actual != null) { throw new AssertException("The value was not null."); } } public static void Same(T actual, T expected) { var isTheSameInstance = ReferenceEquals(actual, expected); if (!isTheSameInstance) { throw new AssertException(string.Format("The expected value '{0}' was not same to the actual value '{1}'", expected, actual)); } } public static T Single(IEnumerable values) { if (values == null) { throw new AssertException("The collection was null."); } if (!values.Any()) { throw new AssertException("The collection contained no values."); } if (values.Count() > 1) { throw new AssertException("The collection contained more than one value."); } return values.First(); } public static IEnumerable Exactly(IEnumerable values, int numberOfOccurrances) { if (values == null) { throw new AssertException("The collection was null."); } var elements = values.Count(); if (elements != numberOfOccurrances) { var message = string.Format( "The collection didn't exactly contain the expected number of occurances.\nActual: {0}\nExpected: {1}", elements, numberOfOccurrances); throw new AssertException(message); } return values; } public static void True(bool condition) { if (!condition) { throw new AssertException("The condition was not true"); } } } } ================================================ FILE: src/Nancy.Testing/Browser.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Helpers; using Nancy.IO; /// /// Provides the capability of executing a request with Nancy, using a specific configuration provided by an instance. /// public class Browser : IHideObjectMembers { private readonly Action defaultBrowserContext; private readonly INancyEngine engine; private readonly INancyEnvironment environment; private readonly IDictionary cookies = new Dictionary(); /// /// Initializes a new instance of the class, with the /// provided configuration. /// /// The configuration that should be used by the bootstrapper. /// The default that should be used in a all requests through this browser object. public Browser(Action action, Action defaults = null) : this(new ConfigurableBootstrapper(action), defaults) { } /// /// Initializes a new instance of the class. /// /// A instance that determines the Nancy configuration that should be used by the browser. /// The default that should be used in a all requests through this browser object. public Browser(INancyBootstrapper bootstrapper, Action defaults = null) { bootstrapper.Initialise(); this.engine = bootstrapper.GetEngine(); this.environment = bootstrapper.GetEnvironment(); this.defaultBrowserContext = defaults ?? DefaultBrowserContext; } /// /// Performs a DELETE request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Delete(string path, Action browserContext = null) { return this.HandleRequest("DELETE", path, browserContext); } /// /// Performs a DELETE request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Delete(Url url, Action browserContext = null) { return this.HandleRequest("DELETE", url, browserContext); } /// /// Performs a GET request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Get(string path, Action browserContext = null) { return this.HandleRequest("GET", path, browserContext); } /// /// Performs a GET request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Get(Url url, Action browserContext = null) { return this.HandleRequest("GET", url, browserContext); } /// /// Performs a HEAD request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Head(string path, Action browserContext = null) { return this.HandleRequest("HEAD", path, browserContext); } /// /// Performs a HEAD request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Head(Url url, Action browserContext = null) { return this.HandleRequest("HEAD", url, browserContext); } /// /// Performs a OPTIONS request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Options(string path, Action browserContext = null) { return this.HandleRequest("OPTIONS", path, browserContext); } /// /// Performs a OPTIONS request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Options(Url url, Action browserContext = null) { return this.HandleRequest("OPTIONS", url, browserContext); } /// /// Performs a PATCH request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Patch(string path, Action browserContext = null) { return this.HandleRequest("PATCH", path, browserContext); } /// /// Performs a PATCH request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Patch(Url url, Action browserContext = null) { return this.HandleRequest("PATCH", url, browserContext); } /// /// Performs a POST request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Post(string path, Action browserContext = null) { return this.HandleRequest("POST", path, browserContext); } /// /// Performs a POST request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Post(Url url, Action browserContext = null) { return this.HandleRequest("POST", url, browserContext); } /// /// Performs a PUT request against Nancy. /// /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Put(string path, Action browserContext = null) { return this.HandleRequest("PUT", path, browserContext); } /// /// Performs a PUT request against Nancy. /// /// The url that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task Put(Url url, Action browserContext = null) { return this.HandleRequest("PUT", url, browserContext); } /// /// Performs a request of the HTTP , on the given , using the /// provided configuration. /// /// HTTP method to send the request as. /// The URl of the request. /// An closure for providing browser context for the request. /// An instance of the executed request. public async Task HandleRequest(string method, Url url, Action browserContext) { var browserContextValues = BuildBrowserContextValues(browserContext ?? (with => { })); var request = CreateRequest(method, url, browserContextValues); var context = await this.engine.HandleRequest(request).ConfigureAwait(false); var response = new BrowserResponse(context, this, (BrowserContext)browserContextValues); this.CaptureCookies(response); return response; } /// /// Performs a request of the HTTP , on the given , using the /// provided configuration. /// /// HTTP method to send the request as. /// The path that is being requested. /// An closure for providing browser context for the request. /// An instance of the executed request. public Task HandleRequest(string method, string path, Action browserContext) { var url = Uri.IsWellFormedUriString(path, UriKind.Relative) ? new Url { Path = path } : (Url)new Uri(path); return this.HandleRequest(method, url, browserContext); } private static void DefaultBrowserContext(BrowserContext context) { context.HttpRequest(); } private void SetCookies(BrowserContext context) { if (!this.cookies.Any()) { return; } var cookieString = this.cookies.Aggregate(string.Empty, (current, cookie) => current + string.Format("{0}={1};", HttpUtility.UrlEncode(cookie.Key), HttpUtility.UrlEncode(cookie.Value))); context.Header("Cookie", cookieString); } private void CaptureCookies(BrowserResponse response) { if (response.Cookies == null || !response.Cookies.Any()) { return; } foreach (var cookie in response.Cookies) { if (string.IsNullOrEmpty(cookie.Value)) { this.cookies.Remove(cookie.Name); } else { this.cookies[cookie.Name] = cookie.Value; } } } private static void BuildRequestBody(IBrowserContextValues contextValues) { if (contextValues.Body != null) { return; } var useFormValues = !string.IsNullOrEmpty(contextValues.FormValues); var bodyContents = useFormValues ? contextValues.FormValues : contextValues.BodyString; var bodyBytes = bodyContents != null ? Encoding.UTF8.GetBytes(bodyContents) : ArrayCache.Empty(); if (useFormValues && !contextValues.Headers.ContainsKey("Content-Type")) { contextValues.Headers["Content-Type"] = new[] { "application/x-www-form-urlencoded" }; } contextValues.Body = new MemoryStream(bodyBytes); } private IBrowserContextValues BuildBrowserContextValues(Action browserContext) { var context = new BrowserContext(this.environment); this.SetCookies(context); this.defaultBrowserContext.Invoke(context); browserContext.Invoke(context); var contextValues = (IBrowserContextValues)context; if (!contextValues.Headers.ContainsKey("user-agent")) { contextValues.Headers.Add("user-agent", new[] { "Nancy.Testing.Browser" }); } return contextValues; } private static Request CreateRequest(string method, Url url, IBrowserContextValues contextValues) { BuildRequestBody(contextValues); var requestStream = RequestStream.FromStream(contextValues.Body, 0, true); var certBytes = contextValues.ClientCertificate ?? null; var requestUrl = url; requestUrl.Scheme = string.IsNullOrWhiteSpace(contextValues.Protocol) ? requestUrl.Scheme : contextValues.Protocol; requestUrl.HostName = string.IsNullOrWhiteSpace(contextValues.HostName) ? requestUrl.HostName : contextValues.HostName; requestUrl.Query = string.IsNullOrWhiteSpace(url.Query) ? (contextValues.QueryString ?? string.Empty) : url.Query; return new Request(method, requestUrl, requestStream, contextValues.Headers, contextValues.UserHostAddress, certBytes); } } } ================================================ FILE: src/Nancy.Testing/BrowserContext.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Security.Cryptography.X509Certificates; using Configuration; using Nancy.Helpers; /// /// Defines the context that a instance should run under. /// public class BrowserContext : IBrowserContextValues { /// /// Initializes a new instance of the class, /// with the provided . /// /// An instance. public BrowserContext(INancyEnvironment environment) { this.Environment = environment; this.Values.Headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); this.Values.Protocol = string.Empty; this.Values.QueryString = string.Empty; this.Values.BodyString = string.Empty; this.Values.FormValues = string.Empty; this.Values.HostName = string.Empty; } /// /// Gets the instance used by the . /// /// An instance. public INancyEnvironment Environment { get; private set; } /// /// Gets or sets the that should be sent with the HTTP request. /// /// A that contains the body that should be sent with the HTTP request. Stream IBrowserContextValues.Body { get; set; } /// /// Gets or sets the protocol that should be sent with the HTTP request. /// /// A contains the protocol that should be sent with the HTTP request.. string IBrowserContextValues.Protocol { get; set; } /// /// Gets or sets the querystring /// string IBrowserContextValues.QueryString { get; set; } /// /// Gets or sets the user host name /// string IBrowserContextValues.HostName { get; set; } /// /// Gets or sets the user host address /// string IBrowserContextValues.UserHostAddress { get; set; } /// /// Gets or sets the ClientCertificate /// X509Certificate2 IBrowserContextValues.ClientCertificate { get; set; } /// /// Gets or sets the body string /// string IBrowserContextValues.BodyString { get; set; } /// /// Gets or sets the form values string /// string IBrowserContextValues.FormValues { get; set; } /// /// Gets or sets the headers that should be sent with the HTTP request. /// /// An instance that contains the headers that should be sent with the HTTP request. IDictionary> IBrowserContextValues.Headers { get; set; } /// /// Adds a body to the HTTP request. /// /// A string that should be used as the HTTP request body. public void Body(string body) { this.Values.BodyString = body; } /// /// Adds a body to the HTTP request. /// /// A string that should be used as the HTTP request body. /// Content type of the HTTP request body. public void Body(string body, string contentType) { this.Values.BodyString = body; this.Header("Content-Type", contentType); } /// /// Adds a body to the HTTP request. /// /// A stream that should be used as the HTTP request body. /// Content type of the HTTP request body. Defaults to 'application/octet-stream' public void Body(Stream body, string contentType = null) { this.Values.Body = body; this.Header("Content-Type", contentType ?? "application/octet-stream"); } /// /// Adds an application/x-www-form-urlencoded form value. /// /// The name of the form element. /// The value of the form element. public void FormValue(string key, string value) { if (!string.IsNullOrEmpty(this.Values.BodyString)) { throw new InvalidOperationException("Form value cannot be set as well as body string"); } this.Values.FormValues += string.Format( "{0}{1}={2}", this.Values.FormValues.Length == 0 ? string.Empty : "&", key, HttpUtility.UrlEncode(value)); } /// /// Adds a header to the HTTP request. /// /// The name of the header. /// The value of the header. public void Header(string name, string value) { if (!this.Values.Headers.ContainsKey(name)) { this.Values.Headers.Add(name, new List()); } var values = (List)this.Values.Headers[name]; values.Add(value); this.Values.Headers[name] = values; } /// /// Configures the request to be sent over HTTP. /// public void HttpRequest() { this.Values.Protocol = "http"; } /// /// Configures the request to be sent over HTTPS. /// public void HttpsRequest() { this.Values.Protocol = "https"; } /// /// Adds a query string entry /// public void Query(string key, string value) { this.Values.QueryString += string.Format( "{0}{1}={2}", this.Values.QueryString.Length == 0 ? "?" : "&", key, HttpUtility.UrlEncode(value)); } /// /// Sets the user host address. /// public void UserHostAddress(string userHostAddress) { this.Values.UserHostAddress = userHostAddress; } /// /// Sets the host name. /// /// is the host name of request url string public void HostName(string hostName) { this.Values.HostName = hostName; } /// /// Sets the ClientCertificate to a default embedded certificate /// The default certificate is embedded using the Nancy.Testing.NancyTestingCert.pfx resource name (secured with password "nancy") /// public void Certificate() { X509Certificate2 certificate2; using ( var pkcs12 = typeof (BrowserContext).GetTypeInfo().Assembly .GetManifestResourceStream("Nancy.Testing.Resources.NancyTestingCert.pfx")) { using (var br = new BinaryReader(pkcs12)) { certificate2 = new X509Certificate2(br.ReadBytes((int)pkcs12.Length), "nancy", X509KeyStorageFlags.Exportable); } } this.Values.ClientCertificate = certificate2; } /// /// Sets the ClientCertificate /// /// the certificate in bytes public void Certificate(byte[] certificate) { this.Values.ClientCertificate = new X509Certificate2(certificate); } /// /// Sets the ClientCertificate /// /// the certificate public void Certificate(X509Certificate2 certificate) { this.Values.ClientCertificate = certificate; } /// /// Find a certificate in a store on the computer. /// /// The location of the store (LocalMachine, CurrentUser) /// The name of the store (e.q. My) /// By which field you want to find the certificate (Commonname, Thumbprint, etc) /// The "Common name" or "thumbprint" you are looking for public void Certificate(StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findBy) { var store = new X509Store(storeName, storeLocation); store.Open(OpenFlags.ReadOnly); var certificatesFound = store.Certificates.Find(findType, findBy, false); if (certificatesFound.Count <= 0) { throw new InvalidOperationException(string.Format("No certificates found in {0} {1} with a {2} that looks like \"{3}\"", storeLocation, storeName, findType, findBy)); } this.Values.ClientCertificate = certificatesFound[0]; } private IBrowserContextValues Values { get { return this; } } } } ================================================ FILE: src/Nancy.Testing/BrowserContextExtensions.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using Nancy.Authentication.Forms; using Nancy.Configuration; using Nancy.Extensions; using Nancy.Helpers; using Nancy.Responses; using Nancy.Responses.Negotiation; using Nancy.Xml; /// /// Defines extensions for the type. /// public static class BrowserContextExtensions { /// /// Adds a multipart/form-data encoded request body to the , using the default boundary name. /// /// The that the data should be added to. /// The multipart/form-data encoded data that should be added. public static void MultiPartFormData(this BrowserContext browserContext, BrowserContextMultipartFormData multipartFormData) { MultiPartFormData(browserContext, multipartFormData, BrowserContextMultipartFormData.DefaultBoundaryName); } /// /// Adds a multipart/form-data encoded request body to the . /// /// The that the data should be added to. /// The multipart/form-data encoded data that should be added. /// The name of the boundary to be used public static void MultiPartFormData(this BrowserContext browserContext, BrowserContextMultipartFormData multipartFormData, string boundaryName) { var contextValues = (IBrowserContextValues)browserContext; contextValues.Body = multipartFormData.Body; contextValues.Headers["Content-Type"] = new[] { "multipart/form-data; boundary=" + boundaryName }; } /// /// Adds a application/json request body to the . /// /// The that the data should be added to. /// The model to be serialized to json. /// Optionally opt in to using a different JSON serializer. public static void JsonBody(this BrowserContext browserContext, TModel model, ISerializer serializer = null) { if (serializer == null) { serializer = new DefaultJsonSerializer(browserContext.Environment); } var contextValues = (IBrowserContextValues)browserContext; contextValues.Body = new MemoryStream(); serializer.Serialize("application/json", model, contextValues.Body); browserContext.Header("Content-Type", "application/json"); } /// /// Adds a application/xml request body to the . /// /// The that the data should be added to. /// The model to be serialized to xml. /// Optionally opt in to using a different XML serializer. public static void XMLBody(this BrowserContext browserContext, TModel model, ISerializer serializer = null) { if (serializer == null) { serializer = new DefaultXmlSerializer(browserContext.Environment); } var contextValues = (IBrowserContextValues)browserContext; contextValues.Body = new MemoryStream(); serializer.Serialize("application/xml", model, contextValues.Body); browserContext.Header("Content-Type", "application/xml"); } /// /// Adds basic authorization credentials to the headers of the . /// /// The that the data should be added to. /// The username to be encoded. /// The password to be encoded. public static void BasicAuth(this BrowserContext browserContext, string username, string password) { var credentials = string.Format("{0}:{1}", username, password); var encodedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials)); browserContext.Header("Authorization", "Basic " + encodedCredentials); } /// /// Adds a cookie to the headers of the . /// /// The that the data should be added to. /// The collection of cookies to add to the cookie request header. public static void Cookie(this BrowserContext browserContext, IDictionary cookies) { if (!cookies.Any()) { return; } foreach (var cookie in cookies) { browserContext.Cookie(cookie.Key, cookie.Value); } } /// /// Adds a cookie to the headers of the . /// /// The that the data should be added to. /// The name of the cookie. /// The value of the cookie. public static void Cookie(this BrowserContext browserContext, string key, string value) { var contextValues = (IBrowserContextValues)browserContext; if (!contextValues.Headers.ContainsKey("Cookie")) { contextValues.Headers.Add("Cookie", new List { string.Empty }); } var values = (List)contextValues.Headers["Cookie"]; values[0] += string.Format("{0}={1};", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)); } /// /// Adds a header to indicate this request is an "ajax request" /// /// /// The that the data should be added to. public static void AjaxRequest(this BrowserContext browserContext) { browserContext.Header("X-Requested-With", "XMLHttpRequest"); } /// /// Adds forms authentication cookie to the headers of the . /// /// The that the data should be added to. /// The user identifier /// Current configuration. public static void FormsAuth(this BrowserContext browserContext, Guid userId, FormsAuthenticationConfiguration formsAuthenticationConfiguration) { var encryptedId = formsAuthenticationConfiguration.CryptographyConfiguration.EncryptionProvider.Encrypt(userId.ToString()); var hmacBytes = formsAuthenticationConfiguration.CryptographyConfiguration.HmacProvider.GenerateHmac(encryptedId); var hmacString = Convert.ToBase64String(hmacBytes); var cookieContents = String.Format("{1}{0}", encryptedId, hmacString); Cookie(browserContext, FormsAuthentication.FormsAuthenticationCookieName, cookieContents); } public static void Accept(this BrowserContext browserContext, MediaRange mediaRange) { browserContext.Accept(mediaRange, 1.0m); } public static void Accept(this BrowserContext browserContext, MediaRange mediaRange, decimal quality) { var contextValues = (IBrowserContextValues)browserContext; if (contextValues.Headers.ContainsKey("accept")) { if (contextValues.Headers["accept"].Count().Equals(1)) { if (contextValues.Headers["accept"].Any(x => x.Equals("*/*"))) { contextValues.Headers.Remove("accept"); } } } var mediaTypeWithQuality = string.Concat(mediaRange, ";q=", Convert.ToString(quality, CultureInfo.InvariantCulture)); browserContext.Header("accept", mediaTypeWithQuality); } } } ================================================ FILE: src/Nancy.Testing/BrowserContextMultipartFormData.cs ================================================ namespace Nancy.Testing { using System; using System.IO; using System.Text; /// /// Contains the functionality for setting up a multipart/form-data encoded request body that should be used by an instance. /// public class BrowserContextMultipartFormData { public const string DefaultBoundaryName = "--NancyMultiPartBoundary123124"; private readonly string boundaryName; /// /// Initializes a new instance of the class using the default boundary name /// /// The configuration that should be used to create the multipart/form-data encoded data. public BrowserContextMultipartFormData(Action configuration) : this(configuration, DefaultBoundaryName) { } /// /// Initializes a new instance of the class. /// /// The configuration that should be used to create the multipart/form-data encoded data. /// Boundary name to be used public BrowserContextMultipartFormData(Action configuration, string boundaryName) { this.boundaryName = boundaryName; this.Body = new MemoryStream(); var configurator = new BrowserContextMultipartFormDataConfigurator(this.Body, boundaryName); configuration.Invoke(configurator); this.TerminateBoundary(); this.Body.Position = 0; } /// /// Gets the that should be used by the HTTP request to pass in the multipart/form-data encoded values. /// /// A that contains the multipart/form-data encoded values. public Stream Body { get; private set; } private void TerminateBoundary() { var endBoundary = String.Format("\r\n--{0}--\r\n", this.boundaryName); var encodedHeaders = Encoding.ASCII.GetBytes(endBoundary); this.Body.Write(encodedHeaders, 0, encodedHeaders.Length); } /// /// Provides an API for configuring a multipart/form-data encoded request body. /// public class BrowserContextMultipartFormDataConfigurator { private const string CRLF = "\r\n"; private readonly Stream body; private readonly string boundary; /// /// Initializes a new instance of the class. /// /// The that the values should be written to. /// The multipart/form-data boundary that should be used in the request body. public BrowserContextMultipartFormDataConfigurator(Stream body, string boundary) { this.body = body; this.boundary = boundary; } /// /// Adds a file to the request body. /// /// The name of the file http element that was used to upload the file. /// Name of the file. /// The mime type of file. /// The content of the file public void AddFile(string name, string fileName, string contentType, Stream file) { this.AddFieldHeaders(name, contentType, fileName); this.AddContent(file); } public void AddFormField(string name, string contentType, string data) { this.AddFormField(name, contentType, new MemoryStream(Encoding.ASCII.GetBytes(data))); } public void AddFormField(string name, string contentType, Stream data) { this.AddFieldHeaders(name, contentType); this.AddContent(data); } private void AddContent(Stream data) { data.Position = 0; data.CopyTo(this.body); } private void AddFieldHeaders(string name, string contentType, string filename = null) { var builder = new StringBuilder(); builder.Append(CRLF); builder.Append("--" + this.boundary); builder.Append(CRLF); builder.AppendFormat(@"Content-Disposition: form-data; name=""{0}""", name); if (!String.IsNullOrWhiteSpace(filename)) { builder.AppendFormat(@"; filename=""{0}""", filename); } builder.Append(CRLF); builder.AppendFormat(@"Content-Type: {0}", contentType); builder.Append(CRLF); builder.Append(CRLF); var encodedHeaders = Encoding.ASCII.GetBytes(builder.ToString()); this.body.Write(encodedHeaders, 0, encodedHeaders.Length); } } } } ================================================ FILE: src/Nancy.Testing/BrowserResponse.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using Nancy.Cookies; /// /// The value that is returned from a route that was invoked by a instance. /// public class BrowserResponse { private readonly Browser hostBrowser; private readonly BrowserContext browserContext; private BrowserResponseBodyWrapper body; /// /// Initializes a new instance of the class. /// /// The that was invoked with. /// Host browser object /// An instance. /// The value of the parameter was . public BrowserResponse(NancyContext context, Browser hostBrowser, BrowserContext browserContext) { if (context == null) { throw new ArgumentNullException("context", "The value of the context parameter cannot be null."); } if (hostBrowser == null) { throw new ArgumentNullException("hostBrowser", "The value of the hostBrowser parameter cannot be null."); } this.hostBrowser = hostBrowser; this.browserContext = browserContext; this.Context = context; } /// /// Gets the HTTP response body as a instance. /// /// A instance. public BrowserResponseBodyWrapper Body { get { return this.body ?? (this.body = new BrowserResponseBodyWrapper(this.Context.Response, this.browserContext)); } } /// /// Gets the content-type of the response. /// /// A string containing the content-type. public string ContentType { get { return this.Context.Response.ContentType; } } /// /// Gets the context that the was invoked with. /// /// A instance. public NancyContext Context { get; private set; } /// /// Gets the headers of the response. /// /// A instance, that contains the response headers. public IDictionary Headers { get { return this.Context.Response.Headers; } } /// /// Gets the HTTP status code of the response. /// /// A enumerable value. public HttpStatusCode StatusCode { get { return this.Context.Response.StatusCode; } } /// /// Gets the description of the HTTP status code. /// /// The HTTP status code description. public string ReasonPhrase { get { return this.Context.Response.ReasonPhrase; } } /// /// Gets the cookies from the response /// public IEnumerable Cookies { get { return this.Context.Response.Cookies; } } } } ================================================ FILE: src/Nancy.Testing/BrowserResponseBodyWrapper.cs ================================================ namespace Nancy.Testing { using System.Collections; using System.Collections.Generic; using System.IO; using Nancy.IO; /// /// Wrapper for the HTTP response body that is used by the class. /// public class BrowserResponseBodyWrapper : IEnumerable { private readonly IEnumerable responseBytes; private readonly string contentType; private DocumentWrapper responseDocument; /// /// Initializes a new instance of the class. /// /// The to wrap. /// The of the request that generated the response. public BrowserResponseBodyWrapper(Response response, BrowserContext browserContext) { this.BrowserContext = browserContext; var contentStream = GetContentStream(response); this.responseBytes = contentStream.ToArray(); this.contentType = response.ContentType; } /// /// Gets the content type of the wrapped response. /// /// A string containing the content type. public string ContentType { get { return this.contentType; } } /// /// Gets the of the request that generated the response. /// /// A intance. public BrowserContext BrowserContext { get; private set; } private static MemoryStream GetContentStream(Response response) { var contentsStream = new MemoryStream(); var unclosableStream = new UnclosableStreamWrapper(contentsStream); response.Contents.Invoke(unclosableStream); contentsStream.Position = 0; return contentsStream; } /// /// Gets a for the provided . /// /// The CSS3 selector that should be applied. /// A instance. public QueryWrapper this[string selector] { get { if (this.responseDocument == null) { this.responseDocument = new DocumentWrapper(this.responseBytes); } return this.responseDocument[selector]; } } /// /// Returns an enumerator that iterates through the collection. /// /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() { return this.responseBytes.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } ================================================ FILE: src/Nancy.Testing/BrowserResponseBodyWrapperExtensions.cs ================================================ namespace Nancy.Testing { using System.IO; using System.Linq; using System.Text; using System.Xml; using Nancy.ModelBinding; using Nancy.ModelBinding.DefaultBodyDeserializers; /// /// Extension method for formatting the contents of a . /// public static class BrowserResponseBodyWrapperExtensions { /// /// Gets the HTTP response body wrapped in a . /// /// An instance of the that the extension should be invoked on. /// A representation of the HTTP response body. public static Stream AsStream(this BrowserResponseBodyWrapper bodyWrapper) { return new MemoryStream(bodyWrapper.ToArray()); } /// /// Gets the HTTP response body wrapped in a string. /// /// An instance of the that the extension should be invoked on. /// A string containing the HTTP response body. public static string AsString(this BrowserResponseBodyWrapper bodyWrapper) { return Encoding.UTF8.GetString(bodyWrapper.ToArray()); } /// /// Gets the HTTP response body as a /// /// An instance of the that the extension should be invoked on. /// A representation of the HTTP response body. public static XmlDocument AsXmlDocument(this BrowserResponseBodyWrapper bodyWrapper) { var document = new XmlDocument(); document.LoadXml(bodyWrapper.AsString()); return document; } /// /// Gets the deserialized representation of the JSON in the response body using the default JSON body deserializer. /// /// The type that the JSON response body should be deserialized to. /// An instance of the that the extension should be invoked on. /// A instance representation of the HTTP response body. public static TModel DeserializeJson(this BrowserResponseBodyWrapper bodyWrapper) { var bodyDeserializer = new JsonBodyDeserializer(bodyWrapper.BrowserContext.Environment); return bodyWrapper.Deserialize(bodyDeserializer); } /// /// Gets the deserialized representation of the XML in the response body using the default XML body deserializer. /// /// The type that the XML response body should be deserialized to. /// An instance of the that the extension should be invoked on. /// A instance representation of the HTTP response body. public static TModel DeserializeXml(this BrowserResponseBodyWrapper bodyWrapper) { var bodyDeserializer = new XmlBodyDeserializer(); return bodyWrapper.Deserialize(bodyDeserializer); } /// /// Gets the deserialized representation of the response body using the specified body deserializer. /// /// The type that the response body should be deserialized to. /// An instance of the that the extension should be invoked on. /// An instance of the that should be used to deserialize the response body. /// A instance representation of the HTTP response body. public static TModel Deserialize(this BrowserResponseBodyWrapper bodyWrapper, IBodyDeserializer bodyDeserializer) { var bindingContext = new BindingContext { DestinationType = typeof(TModel) }; return (TModel)bodyDeserializer.Deserialize(bodyWrapper.ContentType, bodyWrapper.AsStream(), bindingContext); } } } ================================================ FILE: src/Nancy.Testing/BrowserResponseExtensions.cs ================================================ namespace Nancy.Testing { using System; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; /// /// Defines extensions for the type. /// public static class BrowserResponseExtensions { /// /// Asserts that a redirect to a certain location took place. /// /// The that the assert should be made on. /// The location that should have been redirected to. /// The string comparer that should be used by the assertion. The default value is . public static void ShouldHaveRedirectedTo(this BrowserResponse response, string location, StringComparison stringComparer = StringComparison.Ordinal) { var validRedirectStatuses = new[] { HttpStatusCode.MovedPermanently, HttpStatusCode.SeeOther, HttpStatusCode.TemporaryRedirect }; if (!validRedirectStatuses.Any(x => x == response.StatusCode)) { throw new AssertException( string.Format("Status code should be one of 'MovedPermanently, SeeOther, TemporaryRedirect', but was {0}.", response.StatusCode)); } if (!response.Headers["Location"].Equals(location, stringComparer)) { throw new AssertException(string.Format("Location should have been: {0}, but was {1}", location, response.Headers["Location"])); } } public static XDocument BodyAsXml(this BrowserResponse response) { using (var contentsStream = new MemoryStream()) { response.Context.Response.Contents.Invoke(contentsStream); contentsStream.Position = 0; return XDocument.Load(contentsStream); } } } } ================================================ FILE: src/Nancy.Testing/ConfigurableBootstrapper.cs ================================================ namespace Nancy.Testing { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Nancy.Bootstrapper; using Nancy.Configuration; using Nancy.Conventions; using Nancy.Culture; using Nancy.Diagnostics; using Nancy.ErrorHandling; using Nancy.Localization; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; using Nancy.Routing; using Nancy.Routing.Constraints; using Nancy.Routing.Trie; using Nancy.Security; using Nancy.TinyIoc; using Nancy.Validation; using Nancy.ViewEngines; /// /// A Nancy bootstrapper that can be configured with either Type or Instance overrides for all Nancy types. /// public class ConfigurableBootstrapper : NancyBootstrapperWithRequestContainerBase, IPipelines, INancyModuleCatalog { private readonly List registeredTypes; private readonly List registeredInstances; private readonly NancyInternalConfiguration configuration; private readonly ConfigurableModuleCatalog catalog; private bool enableAutoRegistration; private readonly List> applicationStartupActions; private readonly List> requestStartupActions; private readonly Assembly nancyAssembly = typeof(NancyEngine).GetTypeInfo().Assembly; private Action configure; private readonly IList> configurationOverrides; /// /// Test project name suffixes that will be stripped from the test name project /// in order to try and resolve the name of the assembly that is under test so /// that all of its references can be loaded into the application domain. /// public static IList TestAssemblySuffixes = new[] { "test", "tests", "unittests", "specs", "specifications" }; private bool allDiscoveredModules; private bool autoRegistrations = true; private bool disableAutoApplicationStartupRegistration; private bool disableAutoRequestStartupRegistration; /// /// Initializes a new instance of the class. /// public ConfigurableBootstrapper() : this(null) { } /// /// Initializes a new instance of the class. /// /// The configuration that should be used by the bootstrapper. public ConfigurableBootstrapper(Action configuration) { this.catalog = new ConfigurableModuleCatalog(); this.configuration = NancyInternalConfiguration.Default.Invoke(this.TypeCatalog); this.registeredTypes = new List(); this.registeredInstances = new List(); this.applicationStartupActions = new List>(); this.requestStartupActions = new List>(); this.configurationOverrides = new List>(); if (configuration != null) { var configurator = new ConfigurableBootstrapperConfigurator(this); configurator.StatusCodeHandler(); configuration.Invoke(configurator); foreach (var configurationOverride in this.configurationOverrides) { configurationOverride.Invoke(this.configuration); } } } /// /// Configures the Nancy environment /// /// The instance to configure public override void Configure(INancyEnvironment environment) { if (this.configure != null) { this.configure.Invoke(environment); } } /// /// Initialise the bootstrapper - can be used for adding pre/post hooks and /// any other initialisation tasks that aren't specifically container setup /// related /// /// Container instance for resolving types if required. protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { base.ApplicationStartup(container, pipelines); foreach (var action in this.applicationStartupActions) { action.Invoke(container, pipelines); } } /// /// Initialise the request - can be used for adding pre/post hooks and /// any other per-request initialisation tasks that aren't specifically container setup /// related /// /// Container /// Current pipelines /// Current context protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) { base.RequestStartup(container, pipelines, context); foreach (var action in this.requestStartupActions) { action.Invoke(container, pipelines, context); } } /// /// Get all NancyModule implementation instances /// /// The current context /// An instance containing instances. public new IEnumerable GetAllModules(NancyContext context) { return base.GetAllModules(context).Union(this.catalog.GetAllModules(context)); } /// /// Get the instance. /// /// An configured instance. /// The boostrapper must be initialised () prior to calling this. public override INancyEnvironment GetEnvironment() { return base.ApplicationContainer.Resolve(); } /// /// Retrieve a specific module instance from the container /// /// Container to use /// Type of the module /// INancyModule instance protected override INancyModule GetModule(TinyIoCContainer container, Type moduleType) { var module = this.catalog.GetModule(moduleType, null); if (module != null) { return module; } container.Register(typeof(INancyModule), moduleType); return container.Resolve(); } private IEnumerable GetModuleRegistrations() { return this.registeredTypes.Where(x => x is ModuleRegistration).Cast(); } private IEnumerable GetTypeRegistrations() { return this.registeredTypes.Where(x => x is TypeRegistration).Cast(); } private IEnumerable GetCollectionTypeRegistrations() { return this.registeredTypes.Where(x => x.GetType() == typeof(CollectionTypeRegistration)).Cast(); } private static string GetSafePathExtension(string name) { return Path.GetExtension(name) ?? string.Empty; } private IEnumerable Resolve() { var types = this.GetTypeRegistrations() .Where(x => x.RegistrationType == typeof(T)) .Select(x => x.ImplementationType) .ToList(); return (types.Any()) ? types : null; } /// /// Nancy internal configuration /// protected override sealed Func InternalConfiguration { get { return x => this.configuration; } } /// /// Nancy conventions /// protected override NancyConventions Conventions { get { var conventions = this.registeredInstances .Where(x => x.RegistrationType == typeof(NancyConventions)) .Select(x => x.Implementation) .Cast() .FirstOrDefault(); return conventions ?? base.Conventions; } } /// /// Gets all available module types /// protected override IEnumerable Modules { get { var moduleRegistrations = this.GetModuleRegistrations().ToList(); if (moduleRegistrations.Any()) { return moduleRegistrations; } return this.allDiscoveredModules ? base.Modules : ArrayCache.Empty(); } } /// /// Gets the available view engine types /// protected override IEnumerable ViewEngines { get { return this.Resolve() ?? base.ViewEngines; } } /// /// Gets the available custom model binders /// protected override IEnumerable ModelBinders { get { return this.Resolve() ?? base.ModelBinders; } } /// /// Gets the available custom type converters /// protected override IEnumerable TypeConverters { get { return this.Resolve() ?? base.TypeConverters; } } /// /// Gets the available custom body deserializers /// protected override IEnumerable BodyDeserializers { get { return this.Resolve() ?? base.BodyDeserializers; } } /// /// Gets all startup tasks /// protected override IEnumerable ApplicationStartupTasks { get { var tasks = base.ApplicationStartupTasks; var user = (this.Resolve() ?? Enumerable.Empty()).ToArray(); if (this.disableAutoApplicationStartupRegistration || user.Any()) { tasks = tasks.Where(x => x.GetTypeInfo().Assembly.GetName().Name.StartsWith("Nancy", StringComparison.OrdinalIgnoreCase)); } return tasks.Union(user); } } /// /// Gets all request startup tasks /// protected override IEnumerable RequestStartupTasks { get { var tasks = base.RequestStartupTasks; var user = (this.Resolve() ?? Enumerable.Empty()).ToArray(); if (this.disableAutoRequestStartupRegistration || user.Any()) { tasks = tasks.Where(x => x.GetTypeInfo().Assembly.GetName().Name.StartsWith("Nancy", StringComparison.OrdinalIgnoreCase)); } return tasks.Union(user); } } /// /// Gets the root path provider /// protected override IRootPathProvider RootPathProvider { get { return new DefaultRootPathProvider(); } } /// /// Configures the container using AutoRegister followed by registration /// of default INancyModuleCatalog and IRouteResolver. /// /// Container instance protected override void ConfigureApplicationContainer(TinyIoCContainer container) { if (this.enableAutoRegistration) { container.AutoRegister(); this.RegisterBootstrapperTypes(container); } RegisterTypesInternal(this.ApplicationContainer, this.GetTypeRegistrations()); RegisterCollectionTypesInternal(this.ApplicationContainer, this.GetCollectionTypeRegistrations()); RegisterInstancesInternal(this.ApplicationContainer, this.registeredInstances); } /// /// Creates a per request child/nested container /// /// Current context /// Request container instance protected override TinyIoCContainer CreateRequestContainer(NancyContext context) { return this.ApplicationContainer.GetChildContainer(); } /// /// Retrieve all module instances from the container /// /// Container to use /// Collection of INancyModule instances protected override IEnumerable GetAllModules(TinyIoCContainer container) { return container.ResolveAll(false); } /// /// Gets the application level container /// /// Container instance protected override TinyIoCContainer GetApplicationContainer() { return new TinyIoCContainer(); } /// /// Resolve INancyEngine /// /// INancyEngine implementation protected override INancyEngine GetEngineInternal() { try { return this.ApplicationContainer.Resolve(); } catch (InvalidOperationException ex) { throw new InvalidOperationException( "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and specified either a module to test, or set AllDiscoveredModules in the ConfigurableBootstrapper. Inspect the innerexception for more details.", ex.InnerException); } } /// /// Gets the diagnostics for initialization /// /// IDiagnostics implementation protected override IDiagnostics GetDiagnostics() { return this.ApplicationContainer.Resolve(); } /// /// Gets all registered startup tasks /// /// An instance containing instances. protected override IEnumerable GetApplicationStartupTasks() { return this.ApplicationContainer.ResolveAll(false); } /// /// Gets all registered request startup tasks /// /// An instance containing instances. protected override IEnumerable RegisterAndGetRequestStartupTasks(TinyIoCContainer container, Type[] requestStartupTypes) { container.RegisterMultiple(typeof(IRequestStartup), requestStartupTypes); return container.ResolveAll(false); } /// /// Gets all registered application registration tasks /// /// An instance containing instances. protected override IEnumerable GetRegistrationTasks() { if (this.autoRegistrations) { return this.ApplicationContainer.ResolveAll(false); } return this.ApplicationContainer.ResolveAll(false) .Where(x => x.GetType().GetTypeInfo().Assembly == nancyAssembly); } /// /// Register the bootstrapper's implemented types into the container. /// This is necessary so a user can pass in a populated container but not have /// to take the responsibility of registering things like INancyModuleCatalog manually. /// /// Application container to register into protected override void RegisterBootstrapperTypes(TinyIoCContainer applicationContainer) { var moduleCatalog = this.registeredInstances .Where(x => x.RegistrationType == typeof(INancyModuleCatalog)) .Select(x => x.Implementation) .Cast() .FirstOrDefault() ?? this; applicationContainer.Register(moduleCatalog); } /// /// Register the default implementations of internally used types into the container as singletons /// /// Container to register into /// Type registrations to register protected override void RegisterTypes(TinyIoCContainer container, IEnumerable typeRegistrations) { var configuredTypes = this.GetTypeRegistrations().ToList(); var filtered = typeRegistrations .Where(x => !configuredTypes.Any(y => y.RegistrationType == x.RegistrationType)) .Where(x => !this.registeredInstances.Any(y => y.RegistrationType == x.RegistrationType)); RegisterTypesInternal(container, filtered); } private static void RegisterTypesInternal(TinyIoCContainer container, IEnumerable filtered) { foreach (var typeRegistration in filtered) { container.Register(typeRegistration.RegistrationType, typeRegistration.ImplementationType).AsSingleton(); } } /// /// Register the various collections into the container as singletons to later be resolved /// by IEnumerable{Type} constructor dependencies. /// /// Container to register into /// Collection type registrations to register protected override void RegisterCollectionTypes(TinyIoCContainer container, IEnumerable collectionTypeRegistrations) { var configuredCollectionTypes = this.GetCollectionTypeRegistrations().ToList(); var filtered = collectionTypeRegistrations .Where(x => !configuredCollectionTypes.Any(y => y.RegistrationType == x.RegistrationType)); RegisterCollectionTypesInternal(container, filtered); } private static void RegisterCollectionTypesInternal(TinyIoCContainer container, IEnumerable filtered) { foreach (var collectionTypeRegistration in filtered) { container.RegisterMultiple(collectionTypeRegistration.RegistrationType, collectionTypeRegistration.ImplementationTypes); } } /// /// Register the given instances into the container /// /// Container to register into /// Instance registration types protected override void RegisterInstances(TinyIoCContainer container, IEnumerable instanceRegistrations) { var configuredInstanceRegistrations = this.GetTypeRegistrations(); var fileteredInstanceRegistrations = instanceRegistrations .Where(x => !this.registeredInstances.Any(y => y.RegistrationType == x.RegistrationType)) .Where(x => !configuredInstanceRegistrations.Any(y => y.RegistrationType == x.RegistrationType)) .ToList(); RegisterInstancesInternal(container, fileteredInstanceRegistrations); } private static void RegisterInstancesInternal(TinyIoCContainer container, IEnumerable fileteredInstanceRegistrations) { foreach (var instanceRegistration in fileteredInstanceRegistrations) { container.Register( instanceRegistration.RegistrationType, instanceRegistration.Implementation); } } /// /// Register the given module types into the request container /// /// Container to register into /// NancyModule types protected override void RegisterRequestContainerModules(TinyIoCContainer container, IEnumerable moduleRegistrationTypes) { foreach (var moduleRegistrationType in moduleRegistrationTypes) { container.Register( typeof(INancyModule), moduleRegistrationType.ModuleType, moduleRegistrationType.ModuleType.FullName). AsSingleton(); } } /// /// Gets the used by th. /// /// An instance. protected override INancyEnvironmentConfigurator GetEnvironmentConfigurator() { return this.ApplicationContainer.Resolve(); } /// /// Registers an instance in the container. /// /// The container to register into. /// The instance to register. protected override void RegisterNancyEnvironment(TinyIoCContainer container, INancyEnvironment environment) { container.Register(environment); } /// /// /// The pre-request hook /// /// /// The PreRequest hook is called prior to processing a request. If a hook returns /// a non-null response then processing is aborted and the response provided is /// returned. /// /// public BeforePipeline BeforeRequest { get { return this.ApplicationPipelines.BeforeRequest; } set { this.ApplicationPipelines.BeforeRequest = value; } } /// /// /// The post-request hook /// /// /// The post-request hook is called after the response is created. It can be used /// to rewrite the response or add/remove items from the context. /// /// public AfterPipeline AfterRequest { get { return this.ApplicationPipelines.AfterRequest; } set { this.ApplicationPipelines.AfterRequest = value; } } /// /// /// The error hook /// /// /// The error hook is called if an exception is thrown at any time during the pipeline. /// If no error hook exists a standard InternalServerError response is returned /// /// public ErrorPipeline OnError { get { return this.ApplicationPipelines.OnError; } set { this.ApplicationPipelines.OnError = value; } } /// /// Provides an API for configuring a instance. /// public class ConfigurableBootstrapperConfigurator { private readonly ConfigurableBootstrapper bootstrapper; /// /// Initializes a new instance of the class. /// /// The bootstrapper that should be configured. public ConfigurableBootstrapperConfigurator(ConfigurableBootstrapper bootstrapper) { this.bootstrapper = bootstrapper; this.Diagnostics(); } public ConfigurableBootstrapperConfigurator AllDiscoveredModules() { this.bootstrapper.allDiscoveredModules = true; return this; } public ConfigurableBootstrapperConfigurator Binder(IBinder binder) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IBinder), binder)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator Binder() where T : IBinder { this.bootstrapper.configurationOverrides.Add(x => x.Binder = typeof(T)); return this; } /// /// Configures the . /// /// The configuration to apply to the environment. /// A reference to the current . public ConfigurableBootstrapperConfigurator Configure(Action configuration) { this.bootstrapper.configure = configuration; return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ContextFactory(INancyContextFactory contextFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyContextFactory), contextFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ContextFactory() where T : INancyContextFactory { this.bootstrapper.configurationOverrides.Add(x => x.ContextFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator DefaultConfigurationProvider() where T : INancyDefaultConfigurationProvider { this.bootstrapper.configurationOverrides.Add(x => x.DefaultConfigurationProviders = new List(new[] { typeof(T) })); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator DefaultConfigurationProviders(params Type[] defaultConfigurationProviders) { this.bootstrapper.configurationOverrides.Add(x => x.DefaultConfigurationProviders = defaultConfigurationProviders); return this; } /// /// Configures the bootstrapper to use the provided instances of . /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator DefaultConfigurationProviders(INancyDefaultConfigurationProvider defaultConfigurationProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyDefaultConfigurationProvider), defaultConfigurationProvider)); return this; } /// /// Configures the bootstrapper to use the provided instances of . /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator DefaultConfigurationProviders(params INancyDefaultConfigurationProvider[] defaultConfigurationProviders) { foreach (var defaultConfigurationProvider in defaultConfigurationProviders) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyDefaultConfigurationProvider), defaultConfigurationProvider)); } return this; } /// /// Configures the bootstrapper to use the provided type as a dependency. /// /// The type of the dependency that should be used registered with the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator Dependency(Type type) { this.bootstrapper.registeredTypes.Add(new TypeRegistration(typeof(T), type)); return this; } /// /// Configures the bootstrapper to register the specified type as a dependency. /// /// The type of the dependency that should be registered with the bootstrapper. /// A reference to the current . /// This method will register the type for all the interfaces it implements and the type itself. public ConfigurableBootstrapperConfigurator Dependency() { this.bootstrapper.registeredTypes.Add(new TypeRegistration(typeof(T), typeof(T))); foreach (var interfaceType in GetSafeInterfaces(typeof(T))) { this.bootstrapper.registeredTypes.Add(new TypeRegistration(interfaceType, typeof(T))); } return this; } /// /// Configures the bootstrapper to use the provided instance as a dependency. /// /// The dependency instance that should be used registered with the bootstrapper. /// A reference to the current . /// This method will register the instance for all the interfaces it implements and the type itself. public ConfigurableBootstrapperConfigurator Dependency(T instance) { this.bootstrapper.registeredInstances.Add(new InstanceRegistration(typeof(T), instance)); var interfacesToRegisterBy = GetSafeInterfaces(instance.GetType()).Where(i => !i.Equals(typeof(T))); foreach (var interfaceType in interfacesToRegisterBy) { this.bootstrapper.registeredInstances.Add(new InstanceRegistration(interfaceType, instance)); } return this; } private static IEnumerable GetSafeInterfaces(Type type) { return type.GetInterfaces().Where(x => x != typeof(IDisposable)); } /// /// Configures the bootstrapper to register the specified types and instances as a dependencies. /// /// An array of maps between the interfaces and instances that should be registered with the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator MappedDependencies(IEnumerable> dependencies) where T : Type where K : class { foreach (var dependency in dependencies) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(dependency.Item1, dependency.Item2)); } return this; } /// /// Configures the bootstrapper to register the specified instances as a dependencies. /// /// The instances of the dependencies that should be registered with the bootstrapper. /// The type that the dependencies should be registered as. /// A reference to the current . public ConfigurableBootstrapperConfigurator Dependencies(params T[] dependencies) { foreach (var dependency in dependencies) { this.Dependency(dependency); } return this; } /// /// Configures the bootstrapper to use the provided types as a dependency. /// /// The types that should be used registered as dependencies with the bootstrapper. /// The type that the dependencies should be registered as. /// A reference to the current . public ConfigurableBootstrapperConfigurator Dependencies(params Type[] dependencies) { foreach (var dependency in dependencies) { this.Dependency(dependency); } return this; } /// /// Enables the auto registration behavior of the bootstrapper /// /// A reference to the current . public ConfigurableBootstrapperConfigurator EnableAutoRegistration() { this.bootstrapper.enableAutoRegistration = true; return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator StatusCodeHandlers(params Type[] statusCodeHandlers) { this.bootstrapper.configurationOverrides.Add(x => x.StatusCodeHandlers = new List(statusCodeHandlers)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator StatusCodeHandler() where T : IStatusCodeHandler { this.bootstrapper.configurationOverrides.Add(x => x.StatusCodeHandlers = new List(new[] { typeof(T) })); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator EnvironmentConfigurator() where T : INancyEnvironmentConfigurator { this.bootstrapper.configurationOverrides.Add(x => x.EnvironmentConfigurator = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator EnvironmentConfigurator(INancyEnvironmentConfigurator environmentConfigurator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyEnvironmentConfigurator), environmentConfigurator)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator EnvironmentFactory(INancyEnvironmentFactory environmentFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyEnvironmentConfigurator), environmentFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator EnvironmentFactory() where T : INancyEnvironmentFactory { this.bootstrapper.configurationOverrides.Add(x => x.EnvironmentFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator FieldNameConverter(IFieldNameConverter fieldNameConverter) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IFieldNameConverter), fieldNameConverter)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator FieldNameConverter() where T : IFieldNameConverter { this.bootstrapper.configurationOverrides.Add(x => x.FieldNameConverter = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ModelBinderLocator(IModelBinderLocator modelBinderLocator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IModelBinderLocator), modelBinderLocator)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ModelBinderLocator() where T : IModelBinderLocator { this.bootstrapper.configurationOverrides.Add(x => x.ModelBinderLocator = typeof(T)); return this; } /// /// Configures the bootstrapper to create a instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator Module() where T : INancyModule { return this.Modules(typeof(T)); } /// /// Configures the bootstrapper to register the provided instance. /// /// The instance to register. /// A reference to the current . public ConfigurableBootstrapperConfigurator Module(INancyModule module) { this.bootstrapper.catalog.RegisterModuleInstance(module); return this; } /// /// Configures the bootstrapper to create instances of the specified types. /// /// The types of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator Modules(params Type[] modules) { var moduleRegistrations = from module in modules select new ModuleRegistration(module); this.bootstrapper.registeredTypes.AddRange(moduleRegistrations); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator NancyEngine(INancyEngine engine) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyEngine), engine)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator NancyEngine() where T : INancyEngine { this.bootstrapper.configurationOverrides.Add(x => x.NancyEngine = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator NancyModuleBuilder(INancyModuleBuilder nancyModuleBuilder) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(INancyModuleBuilder), nancyModuleBuilder)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator NancyModuleBuilder() where T : INancyModuleBuilder { this.bootstrapper.configurationOverrides.Add(x => x.NancyModuleBuilder = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RenderContextFactory(IRenderContextFactory renderContextFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRenderContextFactory), renderContextFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RenderContextFactory() where T : IRenderContextFactory { this.bootstrapper.configurationOverrides.Add(x => x.RenderContextFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestTraceFactory(IRequestTraceFactory requestTraceFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRequestTraceFactory), requestTraceFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestTraceFactory() where T : IRequestTraceFactory { this.bootstrapper.configurationOverrides.Add(x => x.RequestTraceFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseFormatterFactory(IResponseFormatterFactory responseFormatterFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IResponseFormatterFactory), responseFormatterFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseFormatterFactory() where T : IResponseFormatterFactory { this.bootstrapper.configurationOverrides.Add(x => x.ResponseFormatterFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteCache(IRouteCache routeCache) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteCache), routeCache)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteCache() where T : IRouteCache { this.bootstrapper.configurationOverrides.Add(x => x.RouteCache = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteCacheProvider(IRouteCacheProvider routeCacheProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteCacheProvider), routeCacheProvider)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteCacheProvider() where T : IRouteCacheProvider { this.bootstrapper.configurationOverrides.Add(x => x.RouteCacheProvider = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RootPathProvider(IRootPathProvider rootPathProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRootPathProvider), rootPathProvider)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RootPathProvider() where T : IRootPathProvider { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRootPathProvider), typeof(T))); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteInvoker() where T : IRouteInvoker { this.bootstrapper.configurationOverrides.Add(x => x.RouteInvoker = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteInvoker(IRouteInvoker routeInvoker) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteInvoker), routeInvoker)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteResolver(IRouteResolver routeResolver) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteResolver), routeResolver)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteResolver() where T : IRouteResolver { this.bootstrapper.configurationOverrides.Add(x => x.RouteResolver = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ModelValidatorLocator(IModelValidatorLocator modelValidatorLocator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IModelValidatorLocator), modelValidatorLocator)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ModelValidatorLocator() where T : IModelValidatorLocator { this.bootstrapper.configurationOverrides.Add(x => x.ModelValidatorLocator = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestDispatcher(IRequestDispatcher requestDispatcher) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRequestDispatcher), requestDispatcher)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestDispatcher() where T : IRequestDispatcher { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRequestDispatcher), typeof(T))); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResourceAssemblyProvider(IResourceAssemblyProvider resourceAssemblyProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IResourceAssemblyProvider), resourceAssemblyProvider)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResourceAssemblyProvider() where T : IResourceAssemblyProvider { this.bootstrapper.configurationOverrides.Add(x => x.ResourceAssemblyProvider = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResourceReader(IResourceReader resourceReader) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IResourceReader), resourceReader)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResourceReader() where T : IResourceReader { this.bootstrapper.configurationOverrides.Add(x => x.ResourceReader = typeof(T)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteDescriptionProvider() where T : IRouteDescriptionProvider { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRouteDescriptionProvider), typeof(T))); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteDescriptionProvider(IRouteDescriptionProvider routeDescriptionProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteDescriptionProvider), routeDescriptionProvider)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteMetadataProvider(IRouteMetadataProvider routeMetadataProviders) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteMetadataProvider), routeMetadataProviders)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteMetadataProvider() where T : IRouteMetadataProvider { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IRouteMetadataProvider), new[] { typeof(T) })); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteMetadataProviders(params Type[] routeMetadataProviders) { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IRouteMetadataProvider), routeMetadataProviders)); return this; } /// /// Configures the bootstrapper to use the provided instances of . /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteMetadataProviders(params IRouteMetadataProvider[] routeMetadataProviders) { foreach (var routeMetadataProvider in routeMetadataProviders) { this.bootstrapper.registeredTypes.Add( new InstanceRegistration(typeof(IRouteMetadataProvider), routeMetadataProvider)); } return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteSegmentExtractor() where T : IRouteSegmentExtractor { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRouteSegmentExtractor), typeof(T))); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteSegmentExtractor(IRouteSegmentExtractor routeSegmentExtractor) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteSegmentExtractor), routeSegmentExtractor)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseProcessor() where T : IResponseProcessor { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IResponseProcessor), new[] { typeof(T) })); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseProcessors(params Type[] responseProcessors) { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IResponseProcessor), responseProcessors)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RuntimeEnvironmentInformation(IRuntimeEnvironmentInformation runtimeEnvironmentInformation) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRuntimeEnvironmentInformation), runtimeEnvironmentInformation)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RuntimeEnvironmentInformation() where T : IRuntimeEnvironmentInformation { this.bootstrapper.configurationOverrides.Add(x => x.RuntimeEnvironmentInformation = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator TextResource(ITextResource textResource) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(ITextResource), textResource)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator TextResource() where T : ITextResource { this.bootstrapper.configurationOverrides.Add(x => x.TextResource = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewCache(IViewCache viewCache) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewCache), viewCache)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewCache() where T : IViewCache { this.bootstrapper.configurationOverrides.Add(x => x.ViewCache = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewEngine(IViewEngine viewEngine) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewEngine), viewEngine)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewEngine() where T : IViewEngine { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IViewEngine), new[] { typeof(T) })); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewEngines(params Type[] viewEngines) { this.bootstrapper.registeredTypes.Add( new CollectionTypeRegistration(typeof(IViewEngine), viewEngines)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewFactory(IViewFactory viewFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewFactory), viewFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewFactory() where T : IViewFactory { this.bootstrapper.configurationOverrides.Add(x => x.ViewFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewLocationProvider(IViewLocationProvider viewLocationProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewLocationProvider), viewLocationProvider)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewLocationProvider() where T : IViewLocationProvider { this.bootstrapper.configurationOverrides.Add(x => x.ViewLocationProvider = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewLocator(IViewLocator viewLocator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewLocator), viewLocator)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewLocator() where T : IViewLocator { this.bootstrapper.configurationOverrides.Add(x => x.ViewLocator = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewResolver(IViewResolver viewResolver) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IViewResolver), viewResolver)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ViewResolver() where T : IViewResolver { this.bootstrapper.configurationOverrides.Add(x => x.ViewResolver = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator CsrfTokenValidator(ICsrfTokenValidator tokenValidator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(ICsrfTokenValidator), tokenValidator)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator CsrfTokenValidator() where T : ICsrfTokenValidator { this.bootstrapper.configurationOverrides.Add(x => x.CsrfTokenValidator = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ObjectSerializer(IObjectSerializer objectSerializer) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IObjectSerializer), objectSerializer)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ObjectSerializer() where T : IObjectSerializer { this.bootstrapper.configurationOverrides.Add(x => x.ObjectSerializer = typeof(T)); return this; } /// /// Configures the bootstrapper to use a specific serializer /// /// Serializer type /// A reference to the current . public ConfigurableBootstrapperConfigurator Serializer() where T : ISerializer { this.bootstrapper.configurationOverrides.Add(x => x.Serializers = new List { typeof(T) }); return this; } /// /// Configures the bootstrapper to use specific serializers /// /// Collection of serializer types /// A reference to the current . public ConfigurableBootstrapperConfigurator Serializers(params Type[] serializers) { this.bootstrapper.configurationOverrides.Add(x => x.Serializers = new List(serializers)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator Diagnostics(IDiagnostics diagnostics) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IDiagnostics), diagnostics)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator Diagnostics() where T : IDiagnostics { this.bootstrapper.configurationOverrides.Add(x => x.Diagnostics = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator CultureService(ICultureService cultureService) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(ICultureService), cultureService)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator CultureService() where T : ICultureService { this.bootstrapper.configurationOverrides.Add(x => x.CultureService = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator StaticContentProvider(IStaticContentProvider staticContentProvider) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IStaticContentProvider), staticContentProvider)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator StaticContentProvider() where T : IStaticContentProvider { this.bootstrapper.configurationOverrides.Add(x => x.StaticContentProvider = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteResolverTrie(IRouteResolverTrie routeResolverTrie) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IRouteResolverTrie), routeResolverTrie)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteResolverTrie() where T : IRouteResolverTrie { this.bootstrapper.configurationOverrides.Add(x => x.RouteResolverTrie = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator TrieNodeFactory(ITrieNodeFactory nodeFactory) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(ITrieNodeFactory), nodeFactory)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator TrieNodeFactory() where T : ITrieNodeFactory { this.bootstrapper.configurationOverrides.Add(x => x.TrieNodeFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteSegmentConstraint() where T : IRouteSegmentConstraint { this.bootstrapper.configurationOverrides.Add(x => x.RouteSegmentConstraints = new List { typeof(T) }); return this; } /// /// Configures the bootstrapper to use specific route segment constraints. /// /// Collection of route segment constraint types. /// A reference to the current . public ConfigurableBootstrapperConfigurator RouteSegmentConstraints(params Type[] types) { this.bootstrapper.configurationOverrides.Add(x => x.RouteSegmentConstraints = new List(types)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseNegotiator(IResponseNegotiator negotiator) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(IResponseNegotiator), negotiator)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ResponseNegotiator() where T : IResponseNegotiator { this.bootstrapper.configurationOverrides.Add(x => x.ResponseNegotiator = typeof(T)); return this; } /// /// Configures the bootstrapper to create an instance of the specified type. /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator SerializerFactory() where T : ISerializerFactory { this.bootstrapper.configurationOverrides.Add(x => x.SerializerFactory = typeof(T)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The instance that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator SerializerFactory(ISerializerFactory serializer) { this.bootstrapper.registeredInstances.Add( new InstanceRegistration(typeof(ISerializerFactory), serializer)); return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator ApplicationStartupTask() where T : IApplicationStartup { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IApplicationStartup), typeof(T))); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator ApplicationStartupTasks(params Type[] applicationStartupTypes) { foreach (var type in applicationStartupTypes) { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IApplicationStartup), type)); } return this; } /// /// Configures the bootstrapper to use the provided instance of . /// /// The type of the that the bootstrapper should use. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestStartupTask() where T : IRequestStartup { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRequestStartup), typeof(T))); return this; } /// /// Configures the bootstrapper to use the provided types. /// /// The types that should be used by the bootstrapper. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestStartupTasks(params Type[] requestStartupTypes) { foreach (var type in requestStartupTypes) { this.bootstrapper.registeredTypes.Add( new TypeRegistration(typeof(IRequestStartup), type)); } return this; } /// /// Disables automatic registration of user-defined instances. It /// will not prevent auto-registration of implementations bundled with Nancy. /// /// A reference to the current . public ConfigurableBootstrapperConfigurator DisableAutoApplicationStartupRegistration() { this.bootstrapper.disableAutoApplicationStartupRegistration = true; return this; } /// /// Disables automatic registration of user-defined instances. It /// will not prevent auto-registration of implementations bundled with Nancy. /// /// A reference to the current . public ConfigurableBootstrapperConfigurator DisableAutoRequestStartupRegistration() { this.bootstrapper.disableAutoRequestStartupRegistration = true; return this; } /// /// Adds a hook to the application startup pipeline. This can be called multiple times to add /// more hooks. /// /// The pipeline hook. /// A reference to the current . public ConfigurableBootstrapperConfigurator ApplicationStartup(Action action) { this.bootstrapper.applicationStartupActions.Add(action); return this; } /// /// Adds a hook to the request startup pipeline. This can be called multiple times to add /// more hooks. /// /// The pipeline hook. /// A reference to the current . public ConfigurableBootstrapperConfigurator RequestStartup(Action action) { this.bootstrapper.requestStartupActions.Add(action); return this; } /// /// Disables registrations performed by instances. /// /// A reference to the current . public ConfigurableBootstrapperConfigurator DisableAutoRegistrations() { this.bootstrapper.autoRegistrations = false; return this; } } /// /// Provides the functionality to register instances in a . /// public class ConfigurableModuleCatalog : INancyModuleCatalog { private readonly IDictionary moduleInstances; /// /// Initializes a new instance of the class. /// public ConfigurableModuleCatalog() { this.moduleInstances = new Dictionary(); } /// /// Get all NancyModule implementation instances - should be per-request lifetime /// /// The current context /// An instance containing instances. public IEnumerable GetAllModules(NancyContext context) { return this.moduleInstances.Values; } /// /// Retrieves a specific implementation - should be per-request lifetime /// /// Module type /// The current context /// The instance public INancyModule GetModule(Type moduleType, NancyContext context) { INancyModule module; return this.moduleInstances.TryGetValue(moduleType.FullName, out module) ? module : null; } /// /// Registers a instance. /// /// The instance to register. public void RegisterModuleInstance(INancyModule module) { this.moduleInstances.Add(module.GetType().FullName, module); } } } } ================================================ FILE: src/Nancy.Testing/ConfigurableNancyModule.cs ================================================ namespace Nancy.Testing { using System; using System.Threading; using System.Threading.Tasks; /// /// Provides a way to define a Nancy module though an API. /// public class ConfigurableNancyModule : NancyModule { /// /// Initializes a new instance of the class. /// public ConfigurableNancyModule() { } /// /// Initializes a new instance of the class. /// /// The configuration of the module. public ConfigurableNancyModule(Action closure) : this(string.Empty, closure) { } /// /// Initializes a new instance of the class. /// /// The path that all routes in the module should be relative too. /// The configuration of the module. public ConfigurableNancyModule(string modulePath, Action closure) : base(modulePath) { var configurator = new ConfigurableNancyModuleConfigurator(this); closure.Invoke(configurator); } /// /// Provides an API for configuring a instance. /// public class ConfigurableNancyModuleConfigurator : IHideObjectMembers { private readonly ConfigurableNancyModule wrappedModule; /// /// Initializes a new instance of the class. /// /// The that should be configured. public ConfigurableNancyModuleConfigurator(ConfigurableNancyModule module) { this.wrappedModule = module; } /// /// Adds an after-request process pipeline to the wrappedModule. /// /// An instance. /// An instance to the current . public ConfigurableNancyModuleConfigurator After(AfterPipeline after) { this.wrappedModule.After = after; return this; } /// /// Adds a before-request process pipeline to the wrappedModule. /// /// An instance. /// An instance to the current . public ConfigurableNancyModuleConfigurator Before(BeforePipeline before) { this.wrappedModule.Before = before; return this; } /// /// Adds a route that is valid for DELETE requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Delete(string path, Func condition = null, string name = null) { return this.Delete(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func action, Func condition = null, string name = null) { return this.Delete(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func action, Func condition = null, string name = null) { return this.Delete(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func> action, Func condition = null, string name = null) { return this.Delete(path, action, condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func> action, Func condition = null, string name = null) { return this.Delete(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func> action, Func condition = null, string name = null) { return this.Delete(path, action, condition, name); } /// /// Adds a route that is valid for DELETE requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Delete(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Delete(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for GET requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Get(string path, Func condition = null, string name = null) { return this.Get(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Get(string path, Func action, Func condition = null, string name = null) { return this.Get(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Get(string path, Func action, Func condition = null, string name = null) { return this.Get(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Get(string path, Func> action, Func condition = null, string name = null) { return this.Get(path, action, condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Get(string path, Func> action, Func condition = null, string name = null) { return this.Get(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Get(string path, Func> action, Func condition = null, string name = null) { return this.Get(path, action, condition, name); } /// /// Adds a route that is valid for GET requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Get(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Get(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for HEAD requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Head(string path, Func condition = null, string name = null) { return this.Head(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Head(string path, Func action, Func condition = null, string name = null) { return this.Head(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Head(string path, Func action, Func condition = null, string name = null) { return this.Head(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Head(string path, Func> action, Func condition = null, string name = null) { return this.Head(path, action, condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Head(string path, Func> action, Func condition = null, string name = null) { return this.Head(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Head(string path, Func> action, Func condition = null, string name = null) { return this.Head(path, action, condition, name); } /// /// Adds a route that is valid for HEAD requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Head(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Head(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for OPTIONS requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Options(string path, Func condition = null, string name = null) { return this.Options(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Options(string path, Func action, Func condition = null, string name = null) { return this.Options(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Options(string path, Func action, Func condition = null, string name = null) { return this.Options(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Options(string path, Func> action, Func condition = null, string name = null) { return this.Options(path, action, condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Options(string path, Func> action, Func condition = null, string name = null) { return this.Options(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Options(string path, Func> action, Func condition = null, string name = null) { return this.Options(path, action, condition, name); } /// /// Adds a route that is valid for OPTIONS requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Options(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Options(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for PATCH requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Patch(string path, Func condition = null, string name = null) { return this.Patch(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Patch(string path, Func action, Func condition = null, string name = null) { return this.Patch(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Patch(string path, Func action, Func condition = null, string name = null) { return this.Patch(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Patch(string path, Func> action, Func condition = null, string name = null) { return this.Patch(path, action, condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Patch(string path, Func> action, Func condition = null, string name = null) { return this.Patch(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Patch(string path, Func> action, Func condition = null, string name = null) { return this.Patch(path, action, condition, name); } /// /// Adds a route that is valid for PATCH requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Patch(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Patch(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for POST requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Post(string path, Func condition = null, string name = null) { return this.Post(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Post(string path, Func action, Func condition = null, string name = null) { return this.Post(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Post(string path, Func action, Func condition = null, string name = null) { return this.Post(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Post(string path, Func> action, Func condition = null, string name = null) { return this.Post(path, action, condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Post(string path, Func> action, Func condition = null, string name = null) { return this.Post(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Post(string path, Func> action, Func condition = null, string name = null) { return this.Post(path, action, condition, name); } /// /// Adds a route that is valid for POST requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Post(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Post(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } /// /// Adds a route that is valid for PUT requests. /// /// The path that the route should be registered for. /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . /// This will add a route with a condition that is always evaluates to and an action that returns . public ConfigurableNancyModuleConfigurator Put(string path, Func condition = null, string name = null) { return this.Put(path, (args, module) => HttpStatusCode.OK, condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. public ConfigurableNancyModuleConfigurator Put(string path, Func action, Func condition = null, string name = null) { return this.Put(path, (args, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Put(string path, Func action, Func condition = null, string name = null) { return this.Put(path, (args, module) => Task.FromResult(action((DynamicDictionary)args, module)), condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Put(string path, Func> action, Func condition = null, string name = null) { return this.Put(path, action, condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Put(string path, Func> action, Func condition = null, string name = null) { return this.Put(path, (args, ct, module) => action((DynamicDictionary)args, module), condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Put(string path, Func> action, Func condition = null, string name = null) { return this.Put(path, action, condition, name); } /// /// Adds a route that is valid for PUT requests. /// /// The return type of the route. /// The path that the route should be registered for. /// Action that will be invoked when the route it hit /// A condition to determine if the route can be hit. /// Name of the route. /// An instance to the current . public ConfigurableNancyModuleConfigurator Put(string path, Func> action, Func condition = null, string name = null) { this.wrappedModule.Put(path, (args, ct) => action((DynamicDictionary)args, ct, this.wrappedModule), condition ?? (ctx => true), name ?? string.Empty); return this; } } } } ================================================ FILE: src/Nancy.Testing/DocumentWrapper.cs ================================================ namespace Nancy.Testing { using System.Collections.Generic; using System.IO; using System.Linq; using AngleSharp.Dom.Html; using AngleSharp.Parser.Html; /// /// A basic wrapper around CsQuery /// public class DocumentWrapper { private readonly IHtmlDocument document; /// /// Initializes a new instance of the class. /// /// The document represented as a byte array. public DocumentWrapper(IEnumerable buffer) { var parser = new HtmlParser(); using (var stream = new MemoryStream(buffer.ToArray())) { this.document = parser.Parse(stream); } } /// /// Gets elements from CSS3 selectors /// /// The CSS3 selector that should be applied. /// A instance. public QueryWrapper this[string selector] { get { return new QueryWrapper(this.document.QuerySelectorAll(selector).ToArray()); } } } } ================================================ FILE: src/Nancy.Testing/IBrowserContextValues.cs ================================================ namespace Nancy.Testing { using System.Collections.Generic; using System.IO; using System.Security.Cryptography.X509Certificates; /// /// Provides an API ontop of for extracting values. /// public interface IBrowserContextValues : IHideObjectMembers { /// /// Gets or sets the stream that should be sent with the HTTP request. /// /// A that contains the body that should be sent with the HTTP request. Stream Body { get; set; } /// /// Gets or sets the body string /// string BodyString { get; set; } /// /// Gets or sets the form values string /// /// If is assigned a value, the will be ignored. string FormValues { get; set; } /// /// Gets or sets the headers that should be sent with the HTTP request. /// /// An instance that contains the headers that should be sent with the HTTP request. IDictionary> Headers { get; set; } /// /// Gets or sets the protocol that should be sent with the HTTP request. /// /// A contains the protocol that should be sent with the HTTP request. string Protocol { get; set; } /// /// Gets or sets the querystring /// string QueryString { get; set; } /// /// Gets or sets the basePath string /// string HostName { get; set; } /// /// Gets or sets the user host address /// string UserHostAddress { get; set; } /// /// Gets or sets the ClientCertificate /// X509Certificate2 ClientCertificate { get; set; } } } ================================================ FILE: src/Nancy.Testing/IndexHelper.cs ================================================ namespace Nancy.Testing { using System; /// /// A helper class for providing classes with "named indexers". /// /// The indexer key type /// The indexer return value type. public class IndexHelper { private readonly Func indexDelegate; /// /// Initializes a new instance of the class. /// /// The index delegate. public IndexHelper(Func indexDelegate) { this.indexDelegate = indexDelegate; } /// /// Gets the with the specified key. /// /// The value of the indexer. public TValue this[TKey key] { get { return this.indexDelegate(key); } } } } ================================================ FILE: src/Nancy.Testing/Nancy.Testing.csproj ================================================  Test harness for Nancy applications. $(PackageTags);Testing netstandard2.0;net452 ================================================ FILE: src/Nancy.Testing/NancyContextExtensions.cs ================================================ namespace Nancy.Testing { using System; using System.IO; using System.Xml.Serialization; using Nancy.Extensions; using Nancy.Json; /// /// Defines extensions for the type. /// public static class NancyContextExtensions { private const string DOCUMENT_WRAPPER_KEY_NAME = "@@@@DOCUMENT_WRAPPER@@@@"; private const string JSONRESPONSE_KEY_NAME = "@@@@JSONRESPONSE@@@@"; private const string XMLRESPONSE_KEY_NAME = "@@@@XMLRESPONSE@@@@"; private static T Cache(NancyContext context, string key, Func getData) { // We only really want to generate this once, so we'll stick it in the context // This isn't ideal, but we don't want to hide the guts of the context from the // tests this will have to do. object value; if (context.Items.TryGetValue(key, out value)) { return (T)value; } T data = getData.Invoke(); context.Items[key] = data; return data; } /// /// Returns the HTTP response body, of the specified , wrapped in an instance. /// /// The instance that the HTTP response body should be retrieved from. /// A instance, wrapping the HTTP response body of the context. public static DocumentWrapper DocumentBody(this NancyContext context) { return Cache(context, DOCUMENT_WRAPPER_KEY_NAME, () => { using (var contentsStream = new MemoryStream()) { context.Response.Contents.Invoke(contentsStream); contentsStream.Position = 0; return new DocumentWrapper(contentsStream.GetBufferSegment()); } }); } public static TModel JsonBody(this NancyContext context) { return context.JsonBody(new JavaScriptSerializer()); } public static TModel JsonBody(this NancyContext context, JavaScriptSerializer serializer) { return Cache(context, JSONRESPONSE_KEY_NAME, () => { using (var contentsStream = new MemoryStream()) { context.Response.Contents.Invoke(contentsStream); contentsStream.Position = 0; using (var contents = new StreamReader(contentsStream)) { var model = serializer.Deserialize(contents.ReadToEnd()); return model; } } }); } public static TModel XmlBody(this NancyContext context) { return Cache(context, XMLRESPONSE_KEY_NAME, () => { using (var contentsStream = new MemoryStream()) { context.Response.Contents.Invoke(contentsStream); contentsStream.Position = 0; var serializer = new XmlSerializer(typeof(TModel)); var model = serializer.Deserialize(contentsStream); return (TModel)model; } }); } } } ================================================ FILE: src/Nancy.Testing/NodeWrapper.cs ================================================ namespace Nancy.Testing { using AngleSharp.Dom; /// /// Simple wrapper around a . /// public class NodeWrapper { private readonly IElement element; /// /// Initializes a new instance of the class, for /// the provided . /// /// The dom element that should be wrapped. public NodeWrapper(IElement element) { this.element = element; } /// /// Tests for the presence of an attribute with the specified name. /// /// The name of the attribute to test for. /// True if the node contains an attribute with the specified name, false otherwise. public bool HasAttribute(string name) { return this.element.HasAttribute(name); } /// /// Gets the attributes of the element /// public IndexHelper Attributes { get { return new IndexHelper(x => { var attribute = this.element.Attributes[x]; return (attribute != null) ? attribute.Value : null; }); } } /// /// Gets the inner text of the node. /// /// A containing the inner text of the node. public string InnerText { get { return this.element.TextContent; } } } } ================================================ FILE: src/Nancy.Testing/PassThroughStatusHandler.cs ================================================ namespace Nancy.Testing { using System; using Nancy.ErrorHandling; using Nancy.Extensions; public class PassThroughStatusCodeHandler : IStatusCodeHandler { public bool HandlesStatusCode(HttpStatusCode statusCode, NancyContext context) { Exception exception; if (!context.TryGetException(out exception) || exception == null) { return false; } return statusCode == HttpStatusCode.InternalServerError; } public void Handle(HttpStatusCode statusCode, NancyContext context) { throw new Exception("ConfigurableBootstrapper Exception", context.GetException()); } } } ================================================ FILE: src/Nancy.Testing/PathHelper.cs ================================================ namespace Nancy.Testing { using System; using System.IO; using System.Linq; public static class PathHelper { /// /// Traverses up a directory tree /// /// Start path. /// Levels to climb. /// A containing the new path. public static string GetParent(string path, int levels) { if (string.IsNullOrEmpty(path)) { throw new ArgumentException("path cannot be null or empty", "path"); } if (levels < 0) { throw new ArgumentException("levels cannot be negative", "levels"); } if (levels == 0) { return path; } var parts = path.Split(Path.DirectorySeparatorChar); if (parts.Length <= levels) { throw new InvalidOperationException("Cannot go up beyond the root."); } return parts.Take(parts.Length - levels).Aggregate( (p1, p2) => String.Format("{0}{1}{2}", p1, Path.DirectorySeparatorChar, p2)); } } } ================================================ FILE: src/Nancy.Testing/QueryWrapper.cs ================================================ namespace Nancy.Testing { using System.Collections; using System.Collections.Generic; using System.Linq; using AngleSharp.Dom; /// /// Simple wrapper around a collection of instances. /// public class QueryWrapper : IEnumerable { private readonly IReadOnlyCollection elements; /// /// Initializes a new instance of the class, using /// the provided . /// /// The elements that were the result of query. public QueryWrapper(IReadOnlyCollection elements) { this.elements = elements; } /// /// Gets elements from CSS3 selectors /// /// CSS3 selector /// A instance public QueryWrapper this[string selector] { get { return new QueryWrapper(this.elements.SelectMany(element => element.QuerySelectorAll(selector)).ToArray()); } } /// /// Returns an enumerator that iterates through the collection. /// /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() { return this.elements.Select(element => new NodeWrapper(element)).GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } ================================================ FILE: src/Nancy.Testing/StaticConfigurationContext.cs ================================================ namespace Nancy.Testing { using System; /// /// Helper class for running tests with setup in a certain way. This /// class was designed to be used with a using-statement. Upon disposable the previous static configuration /// values will be reset, leaving it in the state is was before the test. /// public class StaticConfigurationContext : IDisposable { private readonly StaticConfigurationValues existingConfiguration = new StaticConfigurationValues(); /// /// Initializes a new instance of the class. /// /// The configuration context. public StaticConfigurationContext(Action closure) { this.existingConfiguration.CaseSensitive = StaticConfiguration.CaseSensitive; this.existingConfiguration.RequestQueryFormMultipartLimit = StaticConfiguration.RequestQueryFormMultipartLimit; var temporaryConfiguration = new StaticConfigurationValues(); closure.Invoke(temporaryConfiguration); AssignStaticConfigurationValues(temporaryConfiguration); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { AssignStaticConfigurationValues(this.existingConfiguration); } private static void AssignStaticConfigurationValues(StaticConfigurationValues values) { StaticConfiguration.CaseSensitive = values.CaseSensitive; StaticConfiguration.RequestQueryFormMultipartLimit = values.RequestQueryFormMultipartLimit; } /// /// Helper class used to persist state of members. /// public class StaticConfigurationValues { public bool CaseSensitive { get; set; } public int RequestQueryFormMultipartLimit { get; set; } } } } ================================================ FILE: src/Nancy.Testing/TestingViewBrowserResponseExtensions.cs ================================================ namespace Nancy.Testing { /// /// Extension methods for easy access of the properties /// stored in the view context by the testing view factory /// public static class TestingViewBrowserResponseExtensions { /// /// Get the model on the view /// /// the type of the model /// The that the assert should be made on. /// a model of the type /// This method requires that the Browser utilize the public static TType GetModel(this BrowserResponse response) { return (TType)response.Context.Items[TestingViewContextKeys.VIEWMODEL]; } /// /// Returns the name of the view /// /// The that the assert should be made on. /// the name of the view /// This method requires that the Browser utilize the public static string GetViewName(this BrowserResponse response) { return GetContextValue(response, TestingViewContextKeys.VIEWNAME); } /// /// Returns the name of the module /// /// The that the assert should be made on. /// the name of the module /// This method requires that the Browser utilize the public static string GetModuleName(this BrowserResponse response) { return GetContextValue(response, TestingViewContextKeys.MODULENAME); } /// /// Returns the name of the module /// /// The that the assert should be made on. /// the name of the module /// This method requires that the Browser utilize the public static string GetModulePath(this BrowserResponse response) { return GetContextValue(response, TestingViewContextKeys.MODULEPATH); } private static string GetContextValue(BrowserResponse response, string key) { object val; if (!response.Context.Items.TryGetValue(key, out val)) { return string.Empty; } var value = (string)val; return string.IsNullOrEmpty(value) ? string.Empty : value; } } } ================================================ FILE: src/Nancy.Testing/TestingViewContextKeys.cs ================================================ namespace Nancy.Testing { /// /// The key names for where the testing view context data is stored /// public static class TestingViewContextKeys { /// /// The key in ViewLocationContext.Item for the view model /// public const string VIEWMODEL = "__Nancy_Testing_ViewModel"; /// /// The key in ViewLocationContext.Item for the view name /// public const string VIEWNAME = "__Nancy_Testing_ViewName"; /// /// The key in ViewLocationContext.Item for the model name /// public const string MODULENAME = "__Nancy_Testing_ModuleName"; /// /// The key in ViewLocationContext.Item for the module path /// public const string MODULEPATH = "__Nancy_Testing_ModulePath"; } } ================================================ FILE: src/Nancy.Testing/TestingViewFactory.cs ================================================ namespace Nancy.Testing { using Nancy.ViewEngines; /// /// A view factory decorator, aimed for test, /// that exposes some interesting properties about the generated view /// public class TestingViewFactory : IViewFactory { private readonly DefaultViewFactory decoratedViewFactory; /// /// Creates the view based on the view factory sent to the constructor /// /// the view factory that is decorated public TestingViewFactory(DefaultViewFactory viewFactory) { this.decoratedViewFactory = viewFactory; } /// /// Renders the view and then call into the viewfactory /// that the TestingViewFactory is decorating /// /// The name of the view to render. /// The module path of the module that is rendering the view. /// A instance, containing information about the context for which the view is being rendered. /// A response. public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext) { // Intercept and store interesting stuff viewLocationContext.Context.Items[TestingViewContextKeys.VIEWMODEL] = model; viewLocationContext.Context.Items[TestingViewContextKeys.VIEWNAME] = viewName; viewLocationContext.Context.Items[TestingViewContextKeys.MODULENAME] = viewLocationContext.ModuleName; viewLocationContext.Context.Items[TestingViewContextKeys.MODULEPATH] = viewLocationContext.ModulePath; return this.decoratedViewFactory.RenderView(viewName, model, viewLocationContext); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DataAnnotationsRegistrations.cs ================================================ namespace Nancy.Validation.DataAnnotations { using Nancy.Bootstrapper; /// /// Application registrations for Data Annotations validation types. /// public class DataAnnotationsRegistrations : Registrations { /// /// Creates a new instance of the class, that performs /// the default registrations of the Data Annotations types. /// /// An instance. public DataAnnotationsRegistrations(ITypeCatalog typeCatalog) : base(typeCatalog) { this.RegisterAll(); this.RegisterWithDefault(typeof(DefaultPropertyValidatorFactory)); this.RegisterWithDefault(typeof(DefaultValidatableObjectAdapter)); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DataAnnotationsValidator.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Collections.Generic; using System.Linq; /// /// The default Data Annotations implementation of . /// public class DataAnnotationsValidator : IModelValidator { private ModelValidationDescriptor descriptor; private readonly IValidatableObjectAdapter validatableObjectAdapter; private readonly IEnumerable validators; /// /// Initializes a new instance of the class. /// /// The type for validation. /// The instance that should be used by the validator. /// The instance that should be used by the validator. public DataAnnotationsValidator(Type typeForValidation, IPropertyValidatorFactory factory, IValidatableObjectAdapter validatableObjectAdapter) { this.ModelType = typeForValidation; this.validatableObjectAdapter = validatableObjectAdapter; this.validators = factory.GetValidators(typeForValidation); } /// /// Gets the description of the validator. /// /// An instance. public ModelValidationDescriptor Description { get { return this.descriptor ?? (this.descriptor = GetModelValidationDescriptor()); } } /// /// Gets the of the model that is being validated by the validator. /// public Type ModelType { get; private set; } /// /// Validates the specified instance. /// /// The instance that should be validated. /// The of the current request. /// A with the result of the validation. public ModelValidationResult Validate(object instance, NancyContext context) { var errors = new List(); foreach (var validator in this.validators) { var results = validator.Validate(instance, context); errors.AddRange(results); } errors.AddRange(this.validatableObjectAdapter.Validate(instance, context)); return new ModelValidationResult(errors); } private ModelValidationDescriptor GetModelValidationDescriptor() { var rules = this.validators.SelectMany(x => x.GetRules()); return new ModelValidationDescriptor(rules, this.ModelType); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DataAnnotationsValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; /// /// A default implementation of an . /// public abstract class DataAnnotationsValidatorAdapter : IDataAnnotationsValidatorAdapter { private static readonly Lazy isRunningOnMono = new Lazy(() => Type.GetType("Mono.Runtime") != null); protected readonly string ruleType; /// /// Initializes a new instance of the class. /// /// Type of the rule. protected DataAnnotationsValidatorAdapter(string ruleType) { this.ruleType = ruleType; } // http://www.mono-project.com/Guide%3a_Porting_Winforms_Applications#Runtime_Conditionals private static bool IsRunningOnMono { get { return isRunningOnMono.Value; } } /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . public abstract bool CanHandle(ValidationAttribute attribute); /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. public virtual IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor) { yield return new ModelValidationRule(ruleType, attribute.FormatErrorMessage, new [] { descriptor == null ? string.Empty : descriptor.Name }); } /// /// Validates the given instance. /// /// The instance that should be validated. /// The that should be handled. /// A instance for the property that is being validated. /// The of the current request. /// An of instances. public virtual IEnumerable Validate(object instance, ValidationAttribute attribute, PropertyDescriptor descriptor, NancyContext context) { var validationContext = new ValidationContext(instance, null, null) { MemberName = descriptor == null ? null : descriptor.Name }; // When running on Mono the Display attribute is not auto populated so for now we do it ourselves if (IsRunningOnMono) { var displayName = this.GetDisplayNameForMember(instance, validationContext.MemberName); if (!string.IsNullOrEmpty(displayName)) { validationContext.DisplayName = displayName; } } if (descriptor != null) { // Display(Name) will auto populate the context, while DisplayName() needs to be manually set if (validationContext.MemberName == validationContext.DisplayName && !string.IsNullOrEmpty(descriptor.DisplayName)) { validationContext.DisplayName = descriptor.DisplayName; } instance = descriptor.GetValue(instance); } var result = attribute.GetValidationResult(instance, validationContext); if (result != null) { yield return this.GetValidationError(result, validationContext, attribute); } } /// /// Gets a instance based on the supplied . /// /// The to create a for. /// The of the supplied . /// The being validated. /// A of member names. protected virtual ModelValidationError GetValidationError(ValidationResult result, ValidationContext context, ValidationAttribute attribute) { return new ModelValidationError(result.MemberNames, result.ErrorMessage); } private DisplayAttribute GetDisplayAttribute(object instance, string memberName) { if (string.IsNullOrEmpty(memberName)) { return null; } var member = instance.GetType().GetProperty(memberName); return member.GetCustomAttributes(typeof(DisplayAttribute), false) .Cast() .FirstOrDefault(); } private string GetDisplayNameForMember(object instance, string memberName) { var attribute = this.GetDisplayAttribute(instance, memberName); string displayName = null; if (attribute != null) { displayName = attribute.GetName(); } return displayName ?? memberName; } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DataAnnotationsValidatorFactory.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Linq; /// /// Creates and for DataAnnotations. /// public class DataAnnotationsValidatorFactory : IModelValidatorFactory { private readonly IPropertyValidatorFactory factory; private readonly IValidatableObjectAdapter validatableObjectAdapter; /// /// Initializes a new instance of the class. /// /// The instance that should be used by the factory. /// The instance that should be used by the factory. public DataAnnotationsValidatorFactory(IPropertyValidatorFactory factory, IValidatableObjectAdapter validatableObjectAdapter) { this.factory = factory; this.validatableObjectAdapter = validatableObjectAdapter; } /// /// Creates a data annotations instance for the given type. /// /// The type. /// An instance. If no data annotation rules were found for the specified then is returned. public IModelValidator Create(Type type) { var validator = new DataAnnotationsValidator(type, this.factory, this.validatableObjectAdapter); return validator.Description.Rules.Any() ? validator : null; } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DefaultPropertyValidatorFactory.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; /// /// Default implementation of the interface. /// public class DefaultPropertyValidatorFactory : IPropertyValidatorFactory { private readonly IEnumerable adapters; /// /// Initializes a new instance of the class. /// /// The instances that are available to the factory. public DefaultPropertyValidatorFactory(IEnumerable adapters) { this.adapters = adapters; } /// /// Gets the instances for the specified . /// /// The that the validators should be retrieved for. /// An instance, containing objects. public IEnumerable GetValidators(Type type) { var typeDescriptor = new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); var results = new List(); results.Add(this.GetTypeValidator(typeDescriptor)); results.AddRange(this.GetPropertyValidators(typeDescriptor)); return results; } private IEnumerable GetPropertyValidators(ICustomTypeDescriptor typeDescriptor) { var propertyDescriptors = typeDescriptor.GetProperties(); foreach (PropertyDescriptor descriptor in propertyDescriptors) { var attributes = descriptor.Attributes.OfType(); var validator = new PropertyValidator { AttributeAdaptors = this.GetAttributeAdaptors(attributes), Descriptor = descriptor }; yield return validator; } } private PropertyValidator GetTypeValidator(ICustomTypeDescriptor typeDescriptor) { var classAttributes = typeDescriptor.GetAttributes().OfType(); var classValidator = new PropertyValidator { AttributeAdaptors = this.GetAttributeAdaptors(classAttributes) }; return classValidator; } private IDictionary> GetAttributeAdaptors(IEnumerable attributes) { var mappings = new Dictionary>(); foreach (var attribute in attributes) { var results = this.GetAdaptersForAttribute(attribute); mappings.Add(attribute, results); } return mappings; } private IEnumerable GetAdaptersForAttribute(ValidationAttribute attribute) { return this.adapters.Where(x => x.CanHandle(attribute)); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/DefaultValidatableObjectAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; /// /// Default adapter for models that implements the interface. /// public class DefaultValidatableObjectAdapter : IValidatableObjectAdapter { /// /// Validates the specified instance. /// /// The instance. /// /// An instance, containing objects. public IEnumerable Validate(object instance, NancyContext context1) { var validateable = instance as IValidatableObject; if (validateable == null) { return Enumerable.Empty(); } var context = new ValidationContext(instance, null, null); var result = validateable.Validate(context); return result.Select(r => new ModelValidationError(r.MemberNames, r.ErrorMessage)); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/IDataAnnotationsValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; /// /// Adapts DataAnnotations validator attributes into Nancy Validators. /// public interface IDataAnnotationsValidatorAdapter { /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . bool CanHandle(ValidationAttribute attribute); /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor); /// /// Validates the given instance. /// /// The instance that should be validated. /// The that should be handled. /// A instance for the property that is being validated. /// The of the current request. /// An of instances. IEnumerable Validate(object instance, ValidationAttribute attribute, PropertyDescriptor descriptor, NancyContext context); } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/IPropertyValidator.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; /// /// Defines the functionality for validating a property. /// public interface IPropertyValidator { /// /// Gets or sets the instances that should be associated with /// each of the that are specified for the property that is being validated. /// IDictionary> AttributeAdaptors { get; set; } /// /// Gets or sets the for the property that is being validated. /// PropertyDescriptor Descriptor { get; set; } /// /// Gets the validation rules for the property that is being validated. /// /// An instance, containing objects. IEnumerable GetRules(); /// /// Gets the validation result for the specified . /// /// The instance that should be validated. /// The of the current request. /// An instance, containing objects. IEnumerable Validate(object instance, NancyContext context); } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/IPropertyValidatorFactory.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Collections.Generic; /// /// Defines the functionality for retrieving instances /// from a specified . /// public interface IPropertyValidatorFactory { /// /// Gets the instances for the specified . /// /// The that the validators should be retrieved for. /// An instance, containing objects. IEnumerable GetValidators(Type type); } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/IValidatableObjectAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel.DataAnnotations; /// /// Defines the functionality for an adapter for models that implements the interface. /// public interface IValidatableObjectAdapter { /// /// Validates the given instance. /// /// The instance. /// The of the current request. /// An of instances. IEnumerable Validate(object instance, NancyContext context); } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/Nancy.Validation.DataAnnotations.csproj ================================================  Adds Data Annotation validation support to Nancy. $(PackageTags);Validation;DataAnnotations net452 ================================================ FILE: src/Nancy.Validation.DataAnnotations/PropertyValidator.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; /// /// Validates a specified property against a set of Data Annotation /// and instances. /// public class PropertyValidator : IPropertyValidator { /// /// Gets or sets the instances that should be associated with /// each of the that are specified for the property that is being validated. /// public IDictionary> AttributeAdaptors { get; set; } /// /// Gets or sets the for the property that is being validated. /// public PropertyDescriptor Descriptor { get; set; } /// /// Gets the validation rules for the property that is being validated. /// /// An instance, containing objects. public IEnumerable GetRules() { var rules = new List(); foreach (var attributeAdapter in this.AttributeAdaptors) { foreach (var adapter in attributeAdapter.Value) { var results = adapter.GetRules(attributeAdapter.Key, this.Descriptor); rules.AddRange(results); } } return rules; } /// /// Gets the validation result for the specified . /// /// The instance that should be validated. /// The of the current request. /// An instance, containing objects. public IEnumerable Validate(object instance, NancyContext context) { var errors = new List(); foreach (var attributeAdapter in this.AttributeAdaptors) { foreach (var adapter in attributeAdapter.Value) { var results = adapter.Validate(instance, attributeAdapter.Key, this.Descriptor, context); errors.AddRange(results); } } return errors; } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/RangeValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Nancy.Validation.Rules; /// /// An adapter for the . /// public class RangeValidatorAdapter : DataAnnotationsValidatorAdapter { /// /// Initializes a new instance of the class. /// public RangeValidatorAdapter() : base("Comparison") { } /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . public override bool CanHandle(ValidationAttribute attribute) { return attribute is RangeAttribute; } /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. public override IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor) { var ra = (RangeAttribute)attribute; yield return new ComparisonValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }, ComparisonOperator.GreaterThanOrEqual, Convert(ra.OperandType, ra.Minimum)); yield return new ComparisonValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }, ComparisonOperator.LessThanOrEqual, Convert(ra.OperandType, ra.Maximum)); } private static object Convert(Type type, object value) { if(value == null) { return null; } if(value is string) { var converter = TypeDescriptor.GetConverter(type); return converter.ConvertFromString((string)value); } return System.Convert.ChangeType(value, type); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/RegexValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Nancy.Validation.Rules; /// /// An adapter for the . /// public class RegexValidatorAdapter : DataAnnotationsValidatorAdapter { /// /// Initializes a new instance of the class. /// public RegexValidatorAdapter() : base("Regex") { } /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . public override bool CanHandle(ValidationAttribute attribute) { return attribute is RegularExpressionAttribute; } /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. public override IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor) { yield return new RegexValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }, ((RegularExpressionAttribute)attribute).Pattern); } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/RequiredValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Nancy.Validation.Rules; /// /// An adapter for the . /// public class RequiredValidatorAdapter : DataAnnotationsValidatorAdapter { /// /// Initializes a new instance of the class. /// public RequiredValidatorAdapter() : base("Required") { } /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . public override bool CanHandle(ValidationAttribute attribute) { return attribute is RequiredAttribute; } /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. public override IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor) { var requiredAttribute = (RequiredAttribute) attribute; yield return new NotNullValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }); if (!requiredAttribute.AllowEmptyStrings) { yield return new NotEmptyValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }); } } } } ================================================ FILE: src/Nancy.Validation.DataAnnotations/StringLengthValidatorAdapter.cs ================================================ namespace Nancy.Validation.DataAnnotations { using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Nancy.Validation.Rules; /// /// An adapter for the . /// public class StringLengthValidatorAdapter : DataAnnotationsValidatorAdapter { /// /// Initializes a new instance of the class. /// public StringLengthValidatorAdapter() : base("StringLength") { } /// /// Gets a boolean that indicates if the adapter can handle the /// provided . /// /// The that should be handled. /// if the attribute can be handles, otherwise . public override bool CanHandle(ValidationAttribute attribute) { return attribute is StringLengthAttribute; } /// /// Gets the rules the adapter provides. /// /// The that should be handled. /// A instance for the property that is being validated. /// An of instances. public override IEnumerable GetRules(ValidationAttribute attribute, PropertyDescriptor descriptor) { yield return new StringLengthValidationRule(attribute.FormatErrorMessage, new[] { descriptor.Name }, ((StringLengthAttribute)attribute).MinimumLength, ((StringLengthAttribute)attribute).MaximumLength); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/AdapterBase.cs ================================================ namespace Nancy.Validation.FluentValidation { using System; using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; /// /// Defines the core functionality of an adapter between Fluent Validation validators and Nancy validation rules. /// public abstract class AdapterBase : IFluentAdapter { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public abstract bool CanHandle(IPropertyValidator validator); /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public abstract IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator); /// /// Gets the name of the members that the validator applied to. /// /// A string containing the name of members that the validator is applied to. protected virtual IEnumerable GetMemberNames(PropertyRule rule) { yield return rule.PropertyName; } /// /// Get the formatted error message of the validator. /// /// A formatted error message string. protected virtual Func FormatMessage(PropertyRule rule, IPropertyValidator validator) { return displayName => { return new MessageFormatter() .AppendPropertyName(displayName ?? rule.GetDisplayName()) .BuildMessage(validator.ErrorMessageSource.GetString()); }; } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/DefaultFluentAdapterFactory.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using System.Linq; using global::FluentValidation.Validators; /// /// Default implementation of the interface. /// public class DefaultFluentAdapterFactory : IFluentAdapterFactory { private readonly IEnumerable adapters; /// /// Initializes a new instance of the class. /// public DefaultFluentAdapterFactory(IEnumerable adapters) { this.adapters = adapters; } /// /// Creates a instance based on the provided . /// /// The for which the adapter should be created. /// An instance. public IFluentAdapter Create(IPropertyValidator propertyValidator) { var adapter = this.adapters.SingleOrDefault(x => x.CanHandle(propertyValidator)); return adapter ?? new FallbackAdapter(); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/EmailAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class EmailAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is EmailValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new RegexValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ((IEmailValidator)validator).Expression); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/EqualAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class EqualAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is EqualValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.Equal, ((EqualValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/ExactLengthAdapater.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class ExactLengthAdapater : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is ExactLengthValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new StringLengthValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ((ExactLengthValidator)validator).Min, ((ExactLengthValidator)validator).Max); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/ExclusiveBetweenAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class ExclusiveBetweenAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is ExclusiveBetweenValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.GreaterThan, ((ExclusiveBetweenValidator)validator).From); yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.LessThan, ((ExclusiveBetweenValidator)validator).To); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/FallbackAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; /// /// Implementation of that will always return /// when is called. This adapter will be used when no other of the available /// adapters are able to handle the validator. /// public class FallbackAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return false; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ModelValidationRule( "Custom", base.FormatMessage(rule, validator), base.GetMemberNames(rule)); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/FluentValidationRegistrations.cs ================================================ namespace Nancy.Validation.FluentValidation { using global::FluentValidation; using Nancy.Bootstrapper; /// /// Application registrations for Fluent Validation types. /// public class FluentValidationRegistrations : Registrations { /// /// Creates a new instance of the class, that performs /// the default registrations of the Fluent Validation types. /// /// An instance. public FluentValidationRegistrations(ITypeCatalog typeCatalog) : base(typeCatalog) { this.Register(typeof(DefaultFluentAdapterFactory)); this.RegisterAll(); this.RegisterAll(); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/FluentValidationValidator.cs ================================================ namespace Nancy.Validation.FluentValidation { using System; using System.Collections.Generic; using System.Linq; using global::FluentValidation; using global::FluentValidation.Internal; using global::FluentValidation.Results; using global::FluentValidation.Validators; /// /// The default Fluent Validation implementation of . /// public class FluentValidationValidator : IModelValidator { private readonly IValidator validator; private readonly IFluentAdapterFactory factory; /// /// Initializes a new instance of the class for the /// specified . /// /// The Fluent Validation validator that should be used. /// Factory for creating adapters for the type that is being validated. /// The type of the model that is being validated. public FluentValidationValidator(IValidator validator, IFluentAdapterFactory factory, Type modelType) { this.ModelType = modelType; this.validator = validator; this.factory = factory; } /// /// Gets the description of the validator. /// /// A instance. public ModelValidationDescriptor Description { get { return CreateDescriptor(); } } /// /// Gets the of the model that is being validated by the validator. /// public Type ModelType { get; private set; } /// /// Validates the specified instance. /// /// The instance that should be validated. /// The of the current request. /// A with the result of the validation. public ModelValidationResult Validate(object instance, NancyContext context) { var result = this.validator.Validate(instance); var errors = GetErrors(result); return new ModelValidationResult(errors); } private ModelValidationDescriptor CreateDescriptor() { var fluentDescriptor = this.validator.CreateDescriptor(); var rules = new List(); var membersWithValidators = fluentDescriptor.GetMembersWithValidators(); foreach (var memberWithValidators in membersWithValidators) { var fluentRules = fluentDescriptor .GetRulesForMember(memberWithValidators.Key) .OfType(); foreach (var rule in fluentRules) { foreach (var v in rule.Validators) { rules.AddRange(GetValidationRule(rule, v)); } } } return new ModelValidationDescriptor(rules, this.ModelType); } private static IEnumerable GetErrors(ValidationResult results) { return results.IsValid ? Enumerable.Empty() : results.Errors.Select(error => new ModelValidationError(new[] { error.PropertyName }, error.ErrorMessage)); } private IEnumerable GetValidationRule(PropertyRule rule, IPropertyValidator propertyValidator) { return this.factory.Create(propertyValidator).GetRules(rule, propertyValidator); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/FluentValidationValidatorFactory.cs ================================================ namespace Nancy.Validation.FluentValidation { using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using global::FluentValidation; /// /// Creates and for Fluent Validation. /// public class FluentValidationValidatorFactory : IModelValidatorFactory { private readonly IFluentAdapterFactory adapterFactory; private readonly IEnumerable validators; /// /// Initializes a new instance of the instance, with the /// provided . /// /// The factory that should be usdd to create instances. /// The instance that are available for validation. public FluentValidationValidatorFactory(IFluentAdapterFactory adapterFactory, IEnumerable validators) { this.adapterFactory = adapterFactory; this.validators = validators; } /// /// Creates a instance for the given type. /// /// The type of the model that is being validated. /// An instance. If no fluent validation rules were found for the specified then is returned. public IModelValidator Create(Type type) { var instance = GetValidatorInstance(type); return (instance != null) ? new FluentValidationValidator(instance, this.adapterFactory, type) : null; } private IValidator GetValidatorInstance(Type type) { var fullType = CreateValidatorType(type); var available = this.validators .Where(validator => fullType.GetTypeInfo().IsAssignableFrom(validator.GetType())) .ToArray(); if (available.Length > 1) { var names = string.Join(", ", available.Select(v => v.GetType().Name)); var message = string.Concat( "Ambiguous choice between multiple validators for type ", type.Name, ". The validators available are: ", names); throw new InvalidOperationException(message); } return available.FirstOrDefault(); } private static Type CreateValidatorType(Type type) { return typeof(AbstractValidator<>).MakeGenericType(type); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/GreaterThanAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class GreaterThanAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is GreaterThanValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.GreaterThan, ((GreaterThanValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/GreaterThanOrEqualAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class GreaterThanOrEqualAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is GreaterThanOrEqualValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.GreaterThanOrEqual, ((GreaterThanOrEqualValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/IFluentAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; /// /// Defines the functionality of a Fluent Validation adapter. /// public interface IFluentAdapter { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . bool CanHandle(IPropertyValidator validator); /// /// Gets the 's for the Fluent Validation validator. /// /// An instance, containing instances. IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator); } } ================================================ FILE: src/Nancy.Validation.FluentValidation/IFluentAdapterFactory.cs ================================================ namespace Nancy.Validation.FluentValidation { using global::FluentValidation.Validators; /// /// Defines the functionality of a factory for creating instances. /// public interface IFluentAdapterFactory { /// /// Creates a instance based on the provided . /// /// The for which the adapter should be created. /// An instance. IFluentAdapter Create(IPropertyValidator propertyValidator); } } ================================================ FILE: src/Nancy.Validation.FluentValidation/InclusiveBetweenAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class InclusiveBetweenAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is InclusiveBetweenValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.GreaterThanOrEqual, ((InclusiveBetweenValidator)validator).From); yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.LessThanOrEqual, ((InclusiveBetweenValidator)validator).To); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/LengthAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class LengthAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is LengthValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new StringLengthValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ((ILengthValidator)validator).Min, ((ILengthValidator)validator).Max); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/LessThanAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class LessThanAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is LessThanValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.LessThan, ((LessThanValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/LessThanOrEqualAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class LessThanOrEqualAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is LessThanOrEqualValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.LessThanOrEqual, ((LessThanOrEqualValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/Nancy.Validation.FluentValidation.csproj ================================================  Adds Fluent Validation support to Nancy. $(PackageTags);Validation;FluentValidation netstandard2.0;net452 ================================================ FILE: src/Nancy.Validation.FluentValidation/NotEmptyAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class NotEmptyAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is NotEmptyValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new NotEmptyValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule)); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/NotEqualAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class NotEqualAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is NotEqualValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new ComparisonValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule), ComparisonOperator.NotEqual, ((NotEqualValidator)validator).ValueToCompare); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/NotNullAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class NotNullAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatibility with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is NotNullValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new NotNullValidationRule( base.FormatMessage(rule, validator), base.GetMemberNames(rule)); } } } ================================================ FILE: src/Nancy.Validation.FluentValidation/RegularExpressionAdapter.cs ================================================ namespace Nancy.Validation.FluentValidation { using System.Collections.Generic; using global::FluentValidation.Internal; using global::FluentValidation.Validators; using Nancy.Validation.Rules; /// /// Adapter between the Fluent Validation and the Nancy validation rules. /// public class RegularExpressionAdapter : AdapterBase { /// /// Gets whether or not the adapter can handle the provided instance. /// /// The instance to check for compatability with the adapter. /// if the adapter can handle the validator, otherwise . public override bool CanHandle(IPropertyValidator validator) { return validator is RegularExpressionValidator; } /// /// Get the instances that are mapped from the fluent validation rule. /// /// An of instances. public override IEnumerable GetRules(PropertyRule rule, IPropertyValidator validator) { yield return new RegexValidationRule( FormatMessage(rule, validator), GetMemberNames(rule), ((IRegularExpressionValidator)validator).Expression); } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/DefaultFileSystemFactory.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using System.Collections.Generic; using global::DotLiquid.FileSystems; /// /// Default implementation of the interface. /// /// This implementation always returns instances of the type. public class DefaultFileSystemFactory : IFileSystemFactory { /// /// Initializes a new instance of the class /// public DefaultFileSystemFactory() { } /// /// Gets a instance for the provided . /// /// The context that the filesystem should be created for. /// /// An instance. public IFileSystem GetFileSystem(ViewEngineStartupContext context, IEnumerable extensions) { return new LiquidNancyFileSystem(context, extensions); } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/DotLiquidRegistrations.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using global::DotLiquid.NamingConventions; using Nancy.Bootstrapper; /// /// Used to register the default naming conventions for the ViewEngine. The naming convention affects DotLiquid Drops and filters. /// See DotLiquid's documentation on the subject for more information. /// /// This can be overridden in a bootstrapper. /// public class DotLiquidRegistrations : Registrations { /// /// Register the RubyNamingConvention as the default. /// /// An instance. public DotLiquidRegistrations(ITypeCatalog typeCatalog) : base(typeCatalog) { this.RegisterWithDefault(typeof(RubyNamingConvention)); } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/DotLiquidViewEngine.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using System; using System.Collections.Generic; using System.IO; using global::DotLiquid; using global::DotLiquid.Exceptions; using global::DotLiquid.FileSystems; using global::DotLiquid.NamingConventions; using Nancy.Extensions; using Nancy.Responses; /// /// View engine for rendering dotLiquid views. /// public class DotLiquidViewEngine : IViewEngine { private readonly IFileSystemFactory fileSystemFactory; private readonly INamingConvention namingConvention; /// /// Initializes a new instance of the class. /// /// Determines the DotLiquid naming convention that will be used for filters and Drops. This will default to the RubyNamingConvention. /// The instance will use the internally. public DotLiquidViewEngine(INamingConvention namingConvention) : this(new DefaultFileSystemFactory(), namingConvention) { } /// /// Initializes a new instance of the class. /// /// Factory used to retrieve the instance that should be used by the engine. /// The naming convention used by filters and DotLiquid's Drops public DotLiquidViewEngine(IFileSystemFactory fileSystemFactory, INamingConvention namingConvention) { this.fileSystemFactory = fileSystemFactory; this.namingConvention = namingConvention; } /// /// Gets the extensions file extensions that are supported by the view engine. /// /// An instance containing the extensions. /// The extensions should not have a leading dot in the name. public IEnumerable Extensions { get { yield return "liquid"; } } /// /// Initialize the view engine (if necessary) /// /// Startup context public void Initialize(ViewEngineStartupContext viewEngineStartupContext) { Template.FileSystem = this.fileSystemFactory.GetFileSystem(viewEngineStartupContext, this.Extensions); Template.NamingConvention = this.namingConvention; } /// /// Renders the view. /// /// A instance, containing information on how to get the view template. /// The model that should be passed into the view /// /// A response public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, IRenderContext renderContext) { Template parsed; Hash hashedModel; HttpStatusCode status; try { // Set the parsed template parsed = renderContext.ViewCache.GetOrAdd( viewLocationResult, x => { using (var reader = viewLocationResult.Contents.Invoke()) return Template.Parse(reader.ReadToEnd()); }); hashedModel = Hash.FromAnonymousObject(new { Model = new DynamicDrop(model), ViewBag = new DynamicDrop(renderContext.Context.ViewBag) }); // If we got past that, we have a good response status = HttpStatusCode.OK; } catch (SyntaxException syntaxException) { // Syntax Exceptions cause a 500 status = HttpStatusCode.InternalServerError; // Build the error message var errorMessage = string.Format("Syntax error in liquid view '{0}'

{1}", string.Format("{0}/{1}.{2}", viewLocationResult.Location, viewLocationResult.Name, viewLocationResult.Extension), syntaxException.Message); // Create the error model with a Nancy DynamicDictionary because i can ;) var errorModel = new DynamicDictionary { new KeyValuePair("ErrorMessage", errorMessage) }; // Hash up the Error model so DotLiquid will understand it hashedModel = Hash.FromAnonymousObject(new { Model = new DynamicDrop(errorModel) }); // Grab the error HTML from the embedded resource and build up the DotLiquid template. var errorHtml = LoadResource(@"500.liquid"); parsed = Template.Parse(errorHtml); } catch (Exception ex) { status = HttpStatusCode.InternalServerError; // Build the error message var errorMessage = string.Format("Error: {0}", ex.Message); // Create the error model with a Nancy DynamicDictionary because i can ;) var errorModel = new DynamicDictionary { new KeyValuePair("ErrorMessage", errorMessage) }; // Hash up the Error model so DotLiquid will understand it hashedModel = Hash.FromAnonymousObject(new { Model = new DynamicDrop(errorModel) }); // Grab the error HTML from the embedded resource var errorHtml = LoadResource(@"500.liquid"); parsed = Template.Parse(errorHtml); } // Build the response return new HtmlResponse(statusCode: status, contents: stream => { parsed.Render(stream, new RenderParameters { LocalVariables = hashedModel, Registers = Hash.FromAnonymousObject(new { nancy = renderContext }) }); }); } private static string LoadResource(string filename) { var resourceStream = typeof(DotLiquidViewEngine).GetAssembly().GetManifestResourceStream(string.Format("Nancy.ViewEngines.DotLiquid.Resources.{0}", filename)); if (resourceStream == null) { return string.Empty; } using (var reader = new StreamReader(resourceStream)) { return reader.ReadToEnd(); } } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/DynamicDrop.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using System; using System.Collections.Generic; using System.Dynamic; using global::DotLiquid; public class DynamicDrop : Drop { private readonly dynamic model; /// /// Initializes a new instance of the class. /// /// The view model. public DynamicDrop(dynamic model) { this.model = GetValidModelType(model); } public override object BeforeMethod(string propertyName) { if (model == null) { return null; } if (string.IsNullOrEmpty(propertyName)) { return null; } Type modelType = this.model.GetType(); object value = null; if(modelType.Equals(typeof(Dictionary))) { value = GetExpandoObjectValue(propertyName); } else if (modelType.Equals(typeof(DynamicDictionary))) { value = GetDynamicDictionaryObjectValue(propertyName); } else { value = GetPropertyValue(propertyName); } return value; } private object GetExpandoObjectValue(string propertyName) { return (!this.model.ContainsKey(propertyName)) ? null : this.model[propertyName]; } private object GetDynamicDictionaryObjectValue(string propertyName) { DynamicDictionaryValue dictionaryValue = this.model[propertyName] as DynamicDictionaryValue; return dictionaryValue == null || !dictionaryValue.HasValue ? null : dictionaryValue.Value; } private object GetPropertyValue(string propertyName) { var property = this.model.GetType().GetProperty(propertyName); return (property == null) ? null : property.GetValue(this.model, null); } private static dynamic GetValidModelType(dynamic model) { if (model == null) { return null; } return model.GetType().Equals(typeof(ExpandoObject)) ? new Dictionary(model, StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase) : model; } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/IFileSystemFactory.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using System.Collections.Generic; using global::DotLiquid.FileSystems; /// /// Factory for creating a instance. /// public interface IFileSystemFactory { /// /// Gets a instance for the provided . /// /// The context that the filesystem should be created for. /// View extensions to search for /// An instance. IFileSystem GetFileSystem(ViewEngineStartupContext context, IEnumerable extensions); } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/LiquidNancyFileSystem.cs ================================================ namespace Nancy.ViewEngines.DotLiquid { using System; using System.Collections.Generic; using System.Linq; using global::DotLiquid; using global::DotLiquid.Exceptions; using global::DotLiquid.FileSystems; using liquid = global::DotLiquid; /// /// implementation around the Nancy templates. /// public class LiquidNancyFileSystem : IFileSystem { private readonly ViewEngineStartupContext viewEngineStartupContext; private readonly IEnumerable extensions; /// /// Initializes a new instance of the class, /// with the provided . /// /// The context that the engine can operate in. /// public LiquidNancyFileSystem(ViewEngineStartupContext context, IEnumerable extensions) { viewEngineStartupContext = context; this.extensions = extensions; } /// /// Reads the content of the template specified by the parameter. /// /// The of the call. /// The name of the template to read. /// The specified template could not be located. /// The content of the template. public string ReadTemplateFile(liquid.Context context, string templateName) { IRenderContext renderContext = context.Registers["nancy"] as IRenderContext; if (renderContext != null) { // Clean up the template name templateName = GetCleanTemplateName(templateName); // Try to find a matching template using established view conventions ViewLocationResult viewLocation = null; if (extensions.Any( s => templateName.EndsWith(s, StringComparison.OrdinalIgnoreCase))) { // The template name does end with a valid extension, just try to find it viewLocation = renderContext.LocateView(templateName, null); } else { // The template name does not end with a valid extension, try all the possibilities foreach (string extension in extensions) { viewLocation = renderContext.LocateView(String.Concat(templateName, ".", extension), null); if (viewLocation != null) break; } } // If we found one, get the template and pass it back // Eventually, it would be better to pass back the actual template from the cache if it's already been parsed // Or to parse here and store it in the cache before passing it back in not if (viewLocation != null) { using (var reader = viewLocation.Contents.Invoke()) return reader.ReadToEnd(); } } throw new FileSystemException("Template file {0} not found", new[] { templateName }); } private string GetCleanTemplateName(string templateName) { return templateName .Replace(@"""", "") .Replace("'", "") .Replace(@"\", "/"); } } } ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/Nancy.ViewEngines.DotLiquid.csproj ================================================  Enables using the DotLiquid view engine with Nancy. $(PackageTags);View Engine;DotLiquid net452 ================================================ FILE: src/Nancy.ViewEngines.DotLiquid/Resources/500.liquid ================================================ Nancy - 500 Internal Server Error
Error Image

500 - Internal Server Error

Something went horribly, horribly wrong while servicing your request.

We're sorry ☹

Error Details

{{ Model.ErrorMessage }}
================================================ FILE: src/Nancy.ViewEngines.Markdown/MarkDownViewEngine.cs ================================================ namespace Nancy.ViewEngines.Markdown { using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using HeyRed.MarkdownSharp; using Nancy.Responses; using Nancy.ViewEngines.SuperSimpleViewEngine; /// /// Viewengine for rendering Markdown /// public class MarkDownViewEngine : IViewEngine { private readonly SuperSimpleViewEngine engineWrapper; /// /// A regex for removing paragraph tags that the parser inserts on unknown content such as @Section['Content'] /// /// /// <p> - matches the literal string "<p>" /// ( - creates a capture group, so that we can get the text back by backreferencing in our replacement string /// @ - matches the literal string "@" /// [^<]* - matches any character other than the "<" character and does this any amount of times /// ) - ends the capture group /// </p> - matches the literal string "</p>" /// private static readonly Regex ParagraphSubstitution = new Regex("

(@[^<]*)

", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// Gets the extensions file extensions that are supported by the view engine. /// /// An instance containing the extensions. /// The extensions should not have a leading dot in the name. public IEnumerable Extensions { get { return new[] { "md", "markdown" }; } } /// /// Initializes a new instance of the class. /// /// The that should be used by the engine. public MarkDownViewEngine(SuperSimpleViewEngine engineWrapper) { this.engineWrapper = engineWrapper; } /// /// Initialise the view engine (if necessary) /// /// Startup context public void Initialize(ViewEngineStartupContext viewEngineStartupContext) { } /// /// Renders the view. /// /// A instance, containing information on how to get the view template. /// The model that should be passed into the view /// The render context. /// A response. public Response RenderView(ViewLocationResult viewLocationResult, dynamic model, IRenderContext renderContext) { var response = new HtmlResponse(); var html = renderContext.ViewCache.GetOrAdd(viewLocationResult, result => { return ConvertMarkdown(viewLocationResult); }); var renderHtml = this.engineWrapper.Render(html, model, new MarkdownViewEngineHost(new NancyViewEngineHost(renderContext), renderContext, this.Extensions)); response.Contents = stream => { var writer = new StreamWriter(stream); writer.Write(renderHtml); writer.Flush(); }; return response; } /// /// Converts the markdown. /// /// /// HTML converted from markdown /// /// /// View location result. /// public string ConvertMarkdown(ViewLocationResult viewLocationResult) { string content; using (var reader = viewLocationResult.Contents.Invoke()) content = reader.ReadToEnd(); if (content.StartsWith("")) { return MarkdownViewengineRender.RenderMasterPage(content); } var parser = new Markdown(); var html = parser.Transform(content); return ParagraphSubstitution.Replace(html, "$1"); } } } ================================================ FILE: src/Nancy.ViewEngines.Markdown/MarkdownViewEngineHost.cs ================================================ namespace Nancy.ViewEngines.Markdown { using System; using System.Collections.Generic; using System.Linq; using HeyRed.MarkdownSharp; using Nancy.ViewEngines.SuperSimpleViewEngine; public class MarkdownViewEngineHost : IViewEngineHost { private readonly IViewEngineHost viewEngineHost; private readonly IRenderContext renderContext; private readonly IEnumerable validExtensions; private readonly Markdown parser; /// /// Initializes a new instance of the class. /// /// A decorator /// The render context. /// The allowed extensions public MarkdownViewEngineHost(IViewEngineHost viewEngineHost, IRenderContext renderContext, IEnumerable viewExtensions) { this.viewEngineHost = viewEngineHost; this.renderContext = renderContext; this.validExtensions = viewExtensions; this.Context = this.renderContext.Context; this.parser = new Markdown(); } /// /// Context object of the host application. /// /// An instance of the context object from the host. public object Context { get; private set; } /// /// Html "safe" encode a string /// /// Input string /// Encoded string public string HtmlEncode(string input) { return this.viewEngineHost.HtmlEncode(input); } /// /// Get the contents of a template /// /// Name/location of the template /// Model to use to locate the template via conventions /// Contents of the template, or null if not found public string GetTemplate(string templateName, object model) { var viewLocationResult = this.renderContext.LocateView(templateName, model); if (viewLocationResult == null) { return "[ERR!]"; } string templateContent; using (var reader = viewLocationResult.Contents.Invoke()) templateContent = reader.ReadToEnd(); if (viewLocationResult.Name.ToLower() == "master" && validExtensions.Any(x => x.Equals(viewLocationResult.Extension, StringComparison.OrdinalIgnoreCase))) { return MarkdownViewengineRender.RenderMasterPage(templateContent); } if (!validExtensions.Any(x => x.Equals(viewLocationResult.Extension, StringComparison.OrdinalIgnoreCase))) { using (var reader = viewLocationResult.Contents.Invoke()) return reader.ReadToEnd(); } return parser.Transform(templateContent); } /// /// Gets a uri string for a named route /// /// Named route name /// Parameters to use to expand the uri string /// Expanded uri string, or null if not found public string GetUriString(string name, params string[] parameters) { return this.viewEngineHost.GetUriString(name, parameters); } /// /// Expands a path to include any base paths /// /// Path to expand /// Expanded path public string ExpandPath(string path) { return this.viewEngineHost.ExpandPath(path); } /// /// Get the anti forgery token form element /// /// String containing the form element public string AntiForgeryToken() { return this.viewEngineHost.AntiForgeryToken(); } } } ================================================ FILE: src/Nancy.ViewEngines.Markdown/MarkdownViewengineRender.cs ================================================ namespace Nancy.ViewEngines.Markdown { using System; using System.Text.RegularExpressions; using HeyRed.MarkdownSharp; public static class MarkdownViewengineRender { /// /// A regex for removing paragraph tags that the parser inserts on unknown content such as @Section['Content'] /// /// /// <p> - matches the literal string "<p>" /// ( - creates a capture group, so that we can get the text back by backreferencing in our replacement string /// @ - matches the literal string "@" /// [^<]* - matches any character other than the "<" character and does this any amount of times /// ) - ends the capture group /// </p> - matches the literal string "</p>" /// private static readonly Regex ParagraphSubstitution = new Regex("

(@[^<]*)

", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// Renders stand alone / master page /// /// Template content /// HTML converted to markdown public static string RenderMasterPage(string templateContent) { var second = templateContent.Substring( templateContent.IndexOf("", StringComparison.OrdinalIgnoreCase), templateContent.IndexOf("", StringComparison.Ordinal) + 1); var header = second + forth; var toConvert = templateContent.Substring(header.Length, (templateContent.IndexOf("", StringComparison.Ordinal) - (templateContent.IndexOf(forth, StringComparison.Ordinal) + forth.Length))); var footer = templateContent.Substring(templateContent.IndexOf("", StringComparison.OrdinalIgnoreCase)); var parser = new Markdown(); var html = parser.Transform(toConvert.Trim()); var serverHtml = ParagraphSubstitution.Replace(html, "$1"); //TODO: The "Replace" is simply for unit testing HTML/MD strings. Probably needs improving return string.Concat(header, serverHtml, footer).Replace("\r\n", "").Replace("\n", "").Replace("\r", ""); } } } ================================================ FILE: src/Nancy.ViewEngines.Markdown/Nancy.ViewEngines.Markdown.csproj ================================================  Enables using Markdown with Nancy. $(PackageTags);View Engine;Markdown netstandard2.0;net452 ================================================ FILE: src/Nancy.ViewEngines.Nustache/Nancy.ViewEngines.Nustache.csproj ================================================  Enables using the Nustache view engine with Nancy. $(PackageTags);View Engine;Nustache net452 ================================================ FILE: src/Nancy.ViewEngines.Nustache/NustacheViewEngine.cs ================================================ namespace Nancy.ViewEngines.Nustache { using System; using System.Collections.Generic; using System.IO; using global::Nustache.Core; using Nancy.Responses; /// /// View engine for rendering nustache views. /// public class NustacheViewEngine : IViewEngine { /// /// Gets the extensions file extensions that are supported by the view engine. /// /// An instance containing the extensions. /// The extensions should not have a leading dot in the name. public IEnumerable Extensions { get { return new[] { "nustache" }; } } /// /// Initialise the view engine (if necessary) /// /// Startup context public void Initialize(ViewEngineStartupContext viewEngineStartupContext) { } private Template GetOrCompileTemplate(ViewLocationResult viewLocationResult, IRenderContext renderContext) { var viewFactory = renderContext.ViewCache.GetOrAdd( viewLocationResult, x => { using (var reader = x.Contents.Invoke()) return this.GetCompiledTemplate(reader); }); var view = viewFactory.Invoke(); return view; } private Func